зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1887335 - Poll=4ms for WebGLSync completion, async-notify child. r=gfx-reviewers,lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D206294
This commit is contained in:
Родитель
f80f8a5040
Коммит
2bfd7faf83
|
@ -3031,7 +3031,7 @@ void ClientWebGLContext::DepthRange(GLclampf zNear, GLclampf zFar) {
|
|||
Run<RPROC(DepthRange)>(zNear, zFar);
|
||||
}
|
||||
|
||||
void ClientWebGLContext::Flush(const bool flushGl) {
|
||||
void ClientWebGLContext::Flush(const bool flushGl) const {
|
||||
const FuncScope funcScope(*this, "flush");
|
||||
if (IsContextLost()) return;
|
||||
|
||||
|
@ -5461,13 +5461,11 @@ void ClientWebGLContext::GetSyncParameter(
|
|||
case LOCAL_GL_SYNC_FLAGS:
|
||||
return JS::NumberValue(0);
|
||||
case LOCAL_GL_SYNC_STATUS: {
|
||||
if (!sync.mSignaled) {
|
||||
const auto res = ClientWaitSync(sync, 0, 0);
|
||||
sync.mSignaled = (res == LOCAL_GL_ALREADY_SIGNALED ||
|
||||
res == LOCAL_GL_CONDITION_SATISFIED);
|
||||
}
|
||||
return JS::NumberValue(sync.mSignaled ? LOCAL_GL_SIGNALED
|
||||
: LOCAL_GL_UNSIGNALED);
|
||||
const auto res = ClientWaitSync(sync, 0, 0);
|
||||
const auto signaled = (res == LOCAL_GL_ALREADY_SIGNALED ||
|
||||
res == LOCAL_GL_CONDITION_SATISFIED);
|
||||
return JS::NumberValue(signaled ? LOCAL_GL_SIGNALED
|
||||
: LOCAL_GL_UNSIGNALED);
|
||||
}
|
||||
default:
|
||||
EnqueueError_ArgEnum("pname", pname);
|
||||
|
@ -5483,12 +5481,41 @@ GLenum ClientWebGLContext::ClientWaitSync(WebGLSyncJS& sync,
|
|||
if (IsContextLost()) return LOCAL_GL_WAIT_FAILED;
|
||||
if (!sync.ValidateUsable(*this, "sync")) return LOCAL_GL_WAIT_FAILED;
|
||||
|
||||
if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
|
||||
static constexpr auto VALID_BITS = LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT;
|
||||
if ((flags | VALID_BITS) != VALID_BITS) {
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
||||
"`flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.");
|
||||
return LOCAL_GL_WAIT_FAILED;
|
||||
}
|
||||
|
||||
const bool canBeAvailable =
|
||||
(sync.mCanBeAvailable || StaticPrefs::webgl_allow_immediate_queries());
|
||||
if (!canBeAvailable) {
|
||||
constexpr uint8_t WARN_AT = 100;
|
||||
if (sync.mNumQueriesBeforeFirstFrameBoundary <= WARN_AT) {
|
||||
sync.mNumQueriesBeforeFirstFrameBoundary += 1;
|
||||
if (sync.mNumQueriesBeforeFirstFrameBoundary == WARN_AT) {
|
||||
EnqueueWarning(
|
||||
"ClientWaitSync must return TIMEOUT_EXPIRED until control has"
|
||||
" returned to the user agent's main loop, but was polled %hhu "
|
||||
"times. Are you spin-locking? (only warns once)",
|
||||
sync.mNumQueriesBeforeFirstFrameBoundary);
|
||||
}
|
||||
}
|
||||
return LOCAL_GL_TIMEOUT_EXPIRED;
|
||||
}
|
||||
|
||||
if (mCompletedSyncId >= sync.mId) {
|
||||
return LOCAL_GL_ALREADY_SIGNALED;
|
||||
}
|
||||
if (flags & LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
|
||||
Flush();
|
||||
}
|
||||
if (!timeout) return LOCAL_GL_TIMEOUT_EXPIRED;
|
||||
|
||||
// -
|
||||
// Fine, time to block:
|
||||
|
||||
const auto ret = [&]() {
|
||||
const auto& inProcess = mNotLost->inProcess;
|
||||
if (inProcess) {
|
||||
|
@ -5506,29 +5533,10 @@ GLenum ClientWebGLContext::ClientWaitSync(WebGLSyncJS& sync,
|
|||
switch (ret) {
|
||||
case LOCAL_GL_CONDITION_SATISFIED:
|
||||
case LOCAL_GL_ALREADY_SIGNALED:
|
||||
sync.mSignaled = true;
|
||||
OnSyncComplete(sync.mId);
|
||||
break;
|
||||
}
|
||||
|
||||
// -
|
||||
|
||||
const bool canBeAvailable =
|
||||
(sync.mCanBeAvailable || StaticPrefs::webgl_allow_immediate_queries());
|
||||
if (!canBeAvailable) {
|
||||
constexpr uint8_t WARN_AT = 100;
|
||||
if (sync.mNumQueriesBeforeFirstFrameBoundary <= WARN_AT) {
|
||||
sync.mNumQueriesBeforeFirstFrameBoundary += 1;
|
||||
if (sync.mNumQueriesBeforeFirstFrameBoundary == WARN_AT) {
|
||||
EnqueueWarning(
|
||||
"ClientWaitSync must return TIMEOUT_EXPIRED until control has"
|
||||
" returned to the user agent's main loop, but was polled %hhu "
|
||||
"times. Are you spin-locking? (only warns once)",
|
||||
sync.mNumQueriesBeforeFirstFrameBoundary);
|
||||
}
|
||||
}
|
||||
return LOCAL_GL_TIMEOUT_EXPIRED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -6614,7 +6622,7 @@ Maybe<Span<uint8_t>> ClientWebGLContext::ValidateArrayBufferView(
|
|||
// ---------------------------
|
||||
|
||||
webgl::ObjectJS::ObjectJS(const ClientWebGLContext& webgl)
|
||||
: mGeneration(webgl.mNotLost), mId(webgl.mNotLost->state.NextId()) {}
|
||||
: mGeneration(webgl.mNotLost), mId(webgl.NextId()) {}
|
||||
|
||||
// -
|
||||
|
||||
|
|
|
@ -134,9 +134,6 @@ class ShaderKeepAlive final {
|
|||
};
|
||||
|
||||
class ContextGenerationInfo final {
|
||||
private:
|
||||
ObjectId mLastId = 0;
|
||||
|
||||
public:
|
||||
webgl::ExtensionBits mEnabledExtensions;
|
||||
RefPtr<WebGLProgramJS> mCurrentProgram;
|
||||
|
@ -181,8 +178,6 @@ class ContextGenerationInfo final {
|
|||
webgl::ProvokingVertex mProvokingVertex = webgl::ProvokingVertex::LastVertex;
|
||||
|
||||
mutable Maybe<std::unordered_map<GLenum, bool>> mIsEnabledMap;
|
||||
|
||||
ObjectId NextId() { return mLastId += 1; }
|
||||
};
|
||||
|
||||
// -
|
||||
|
@ -495,7 +490,6 @@ class WebGLSyncJS final : public nsWrapperCache,
|
|||
|
||||
bool mCanBeAvailable = false;
|
||||
uint8_t mNumQueriesBeforeFirstFrameBoundary = 0;
|
||||
bool mSignaled = false;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSyncJS)
|
||||
|
@ -783,8 +777,11 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
|||
mutable GLenum mNextError = 0;
|
||||
mutable webgl::LossStatus mLossStatus = webgl::LossStatus::Ready;
|
||||
mutable bool mAwaitingRestore = false;
|
||||
mutable webgl::ObjectId mLastId = 0;
|
||||
|
||||
public:
|
||||
webgl::ObjectId NextId() const { return mLastId += 1; }
|
||||
|
||||
// Holds Some Id if async present is used
|
||||
mutable Maybe<layers::RemoteTextureId> mLastRemoteTextureId;
|
||||
mutable Maybe<layers::RemoteTextureOwnerId> mRemoteTextureOwnerId;
|
||||
|
@ -1400,7 +1397,7 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
|||
|
||||
void DepthRange(GLclampf zNear, GLclampf zFar);
|
||||
|
||||
void Flush(bool flushGl = true);
|
||||
void Flush(bool flushGl = true) const;
|
||||
|
||||
void Finish();
|
||||
|
||||
|
@ -2248,6 +2245,13 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
|||
JS::MutableHandle<JS::Value> retval) const;
|
||||
void WaitSync(const WebGLSyncJS&, GLbitfield flags, GLint64 timeout) const;
|
||||
|
||||
mutable webgl::ObjectId mCompletedSyncId = 0;
|
||||
void OnSyncComplete(webgl::ObjectId id) const {
|
||||
if (mCompletedSyncId < id) {
|
||||
mCompletedSyncId = id;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------- Transform Feedback ---------------------------
|
||||
|
||||
void BindTransformFeedback(GLenum target, WebGLTransformFeedbackJS*);
|
||||
|
|
|
@ -180,6 +180,17 @@ void HostWebGLContext::CreateSync(const ObjectId id) {
|
|||
return;
|
||||
}
|
||||
slot = GetWebGL2Context()->FenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
if (!slot) return;
|
||||
|
||||
slot->OnCompleteTaskAdd([host = WeakPtr{this}, id]() {
|
||||
if (!host) return;
|
||||
if (host->mOwnerData.inProcess) {
|
||||
host->mOwnerData.inProcess->OnSyncComplete(id);
|
||||
} else if (host->mOwnerData.outOfProcess) {
|
||||
(void)host->mOwnerData.outOfProcess->SendOnSyncComplete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void HostWebGLContext::CreateTexture(const ObjectId id) {
|
||||
|
|
|
@ -109,6 +109,9 @@ child:
|
|||
|
||||
// Tell client that this queue needs to be shut down
|
||||
async OnContextLoss(ContextLossReason aReason);
|
||||
|
||||
// Triggered when the id from FenceSync completes.
|
||||
async OnSyncComplete(uint64_t id);
|
||||
};
|
||||
|
||||
} // dom
|
||||
|
|
|
@ -108,8 +108,7 @@ class WebGL2Context final : public WebGLContext {
|
|||
1000 * 1000 * 1000; // 1000ms in ns.
|
||||
|
||||
RefPtr<WebGLSync> FenceSync(GLenum condition, GLbitfield flags);
|
||||
GLenum ClientWaitSync(const WebGLSync& sync, GLbitfield flags,
|
||||
GLuint64 timeout);
|
||||
GLenum ClientWaitSync(WebGLSync& sync, GLbitfield flags, GLuint64 timeout);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Transform Feedback - WebGL2ContextTransformFeedback.cpp
|
||||
|
|
|
@ -29,10 +29,11 @@ RefPtr<WebGLSync> WebGL2Context::FenceSync(GLenum condition, GLbitfield flags) {
|
|||
}
|
||||
|
||||
RefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
|
||||
mPendingSyncs.emplace_back(globj);
|
||||
return globj;
|
||||
}
|
||||
|
||||
GLenum WebGL2Context::ClientWaitSync(const WebGLSync& sync, GLbitfield flags,
|
||||
GLenum WebGL2Context::ClientWaitSync(WebGLSync& sync, GLbitfield flags,
|
||||
GLuint64 timeout) {
|
||||
const FuncScope funcScope(*this, "clientWaitSync");
|
||||
if (IsContextLost()) return LOCAL_GL_WAIT_FAILED;
|
||||
|
@ -50,13 +51,47 @@ GLenum WebGL2Context::ClientWaitSync(const WebGLSync& sync, GLbitfield flags,
|
|||
return LOCAL_GL_WAIT_FAILED;
|
||||
}
|
||||
|
||||
const auto ret = gl->fClientWaitSync(sync.mGLName, flags, timeout);
|
||||
const auto ret = sync.ClientWaitSync(flags, timeout);
|
||||
return UnderlyingValue(ret);
|
||||
}
|
||||
|
||||
if (ret == LOCAL_GL_CONDITION_SATISFIED || ret == LOCAL_GL_ALREADY_SIGNALED) {
|
||||
sync.MarkSignaled();
|
||||
void WebGLContext::EnsurePollPendingSyncs_Pending() const {
|
||||
if (mPollPendingSyncs_Pending) return;
|
||||
mPollPendingSyncs_Pending = NS_NewRunnableFunction(
|
||||
"WebGLContext::PollPendingSyncs", [weak = WeakPtr{this}]() {
|
||||
if (const auto strong = RefPtr{weak.get()}) {
|
||||
strong->PollPendingSyncs();
|
||||
}
|
||||
});
|
||||
if (const auto eventTarget = GetCurrentSerialEventTarget()) {
|
||||
eventTarget->DelayedDispatch(do_AddRef(mPollPendingSyncs_Pending),
|
||||
kPollPendingSyncs_DelayMs);
|
||||
} else {
|
||||
NS_WARNING(
|
||||
"[EnsurePollPendingSyncs_Pending] GetCurrentSerialEventTarget() -> "
|
||||
"nullptr");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
void WebGLContext::PollPendingSyncs() const {
|
||||
const FuncScope funcScope(*this, "<pollPendingSyncs>");
|
||||
if (IsContextLost()) return;
|
||||
|
||||
while (mPendingSyncs.size()) {
|
||||
if (const auto sync = RefPtr{mPendingSyncs.front().get()}) {
|
||||
const auto res = sync->ClientWaitSync(0, 0);
|
||||
switch (res) {
|
||||
case ClientWaitSyncResult::WAIT_FAILED:
|
||||
case ClientWaitSyncResult::TIMEOUT_EXPIRED:
|
||||
return;
|
||||
case ClientWaitSyncResult::CONDITION_SATISFIED:
|
||||
case ClientWaitSyncResult::ALREADY_SIGNALED:
|
||||
// Communication back to child happens in sync->lientWaitSync.
|
||||
break;
|
||||
}
|
||||
}
|
||||
mPendingSyncs.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -162,4 +162,11 @@ mozilla::ipc::IPCResult WebGLChild::RecvOnContextLoss(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult WebGLChild::RecvOnSyncComplete(
|
||||
const webgl::ObjectId id) const {
|
||||
if (!mContext) return IPC_OK();
|
||||
mContext->OnSyncComplete(id);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -58,6 +58,7 @@ class WebGLChild final : public PWebGLChild, public SupportsWeakPtr {
|
|||
public:
|
||||
mozilla::ipc::IPCResult RecvJsWarning(const std::string&) const;
|
||||
mozilla::ipc::IPCResult RecvOnContextLoss(webgl::ContextLossReason) const;
|
||||
mozilla::ipc::IPCResult RecvOnSyncComplete(webgl::ObjectId) const;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -881,6 +881,8 @@ void WebGLContext::OnEndOfFrame() {
|
|||
|
||||
mDrawCallsSinceLastFlush = 0;
|
||||
|
||||
PollPendingSyncs();
|
||||
|
||||
BumpLru();
|
||||
}
|
||||
|
||||
|
|
|
@ -303,6 +303,15 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr {
|
|||
uint64_t mNextFenceId = 1;
|
||||
uint64_t mCompletedFenceId = 0;
|
||||
|
||||
mutable std::list<WeakPtr<WebGLSync>> mPendingSyncs;
|
||||
mutable RefPtr<nsIRunnable> mPollPendingSyncs_Pending;
|
||||
static constexpr uint32_t kPollPendingSyncs_DelayMs =
|
||||
4; // Four times a frame.
|
||||
public:
|
||||
void EnsurePollPendingSyncs_Pending() const;
|
||||
void PollPendingSyncs() const;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<gl::Texture> mIncompleteTexOverride;
|
||||
|
||||
public:
|
||||
|
|
|
@ -23,10 +23,39 @@ WebGLSync::~WebGLSync() {
|
|||
mContext->gl->fDeleteSync(mGLName);
|
||||
}
|
||||
|
||||
void WebGLSync::MarkSignaled() const {
|
||||
if (mContext->mCompletedFenceId < mFenceId) {
|
||||
mContext->mCompletedFenceId = mFenceId;
|
||||
ClientWaitSyncResult WebGLSync::ClientWaitSync(const GLbitfield flags,
|
||||
const GLuint64 timeout) {
|
||||
if (!mContext) return ClientWaitSyncResult::WAIT_FAILED;
|
||||
if (IsKnownComplete()) return ClientWaitSyncResult::ALREADY_SIGNALED;
|
||||
|
||||
auto ret = ClientWaitSyncResult::WAIT_FAILED;
|
||||
bool newlyComplete = false;
|
||||
const auto status = static_cast<ClientWaitSyncResult>(
|
||||
mContext->gl->fClientWaitSync(mGLName, 0, 0));
|
||||
switch (status) {
|
||||
case ClientWaitSyncResult::TIMEOUT_EXPIRED: // Poll() -> false
|
||||
case ClientWaitSyncResult::WAIT_FAILED: // Error
|
||||
ret = status;
|
||||
break;
|
||||
case ClientWaitSyncResult::CONDITION_SATISFIED: // Should never happen, but
|
||||
// tolerate it.
|
||||
case ClientWaitSyncResult::ALREADY_SIGNALED: // Poll() -> true
|
||||
newlyComplete = true;
|
||||
ret = status;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newlyComplete) {
|
||||
if (mContext->mCompletedFenceId < mFenceId) {
|
||||
mContext->mCompletedFenceId = mFenceId;
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(mOnCompleteTasks);
|
||||
for (const auto& task : *mOnCompleteTasks) {
|
||||
(*task)();
|
||||
}
|
||||
mOnCompleteTasks = {};
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -11,9 +11,30 @@
|
|||
namespace mozilla {
|
||||
namespace webgl {
|
||||
class AvailabilityRunnable;
|
||||
|
||||
struct Task {
|
||||
virtual ~Task() = default;
|
||||
virtual void operator()() const = 0;
|
||||
};
|
||||
|
||||
template <class F>
|
||||
struct FnTask : public Task {
|
||||
const F fn;
|
||||
|
||||
explicit FnTask(F&& fn) : fn(std::move(fn)) {}
|
||||
|
||||
virtual void operator()() const override { fn(); }
|
||||
};
|
||||
} // namespace webgl
|
||||
|
||||
class WebGLSync final : public WebGLContextBoundObject {
|
||||
enum class ClientWaitSyncResult : GLenum {
|
||||
WAIT_FAILED = LOCAL_GL_WAIT_FAILED,
|
||||
TIMEOUT_EXPIRED = LOCAL_GL_TIMEOUT_EXPIRED,
|
||||
CONDITION_SATISFIED = LOCAL_GL_CONDITION_SATISFIED,
|
||||
ALREADY_SIGNALED = LOCAL_GL_ALREADY_SIGNALED,
|
||||
};
|
||||
|
||||
class WebGLSync final : public WebGLContextBoundObject, public SupportsWeakPtr {
|
||||
friend class WebGL2Context;
|
||||
friend class webgl::AvailabilityRunnable;
|
||||
|
||||
|
@ -23,10 +44,22 @@ class WebGLSync final : public WebGLContextBoundObject {
|
|||
const uint64_t mFenceId;
|
||||
bool mCanBeAvailable = false;
|
||||
|
||||
std::optional<std::vector<std::unique_ptr<webgl::Task>>> mOnCompleteTasks =
|
||||
std::vector<std::unique_ptr<webgl::Task>>{};
|
||||
|
||||
public:
|
||||
WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags);
|
||||
|
||||
void MarkSignaled() const;
|
||||
ClientWaitSyncResult ClientWaitSync(GLbitfield flags, GLuint64 timeout);
|
||||
|
||||
template <class F>
|
||||
void OnCompleteTaskAdd(F&& fn) {
|
||||
MOZ_RELEASE_ASSERT(mOnCompleteTasks);
|
||||
auto task = std::make_unique<webgl::FnTask<F>>(std::move(fn));
|
||||
mOnCompleteTasks->push_back(std::move(task));
|
||||
}
|
||||
|
||||
bool IsKnownComplete() const { return !mOnCompleteTasks; }
|
||||
|
||||
private:
|
||||
~WebGLSync() override;
|
||||
|
|
Загрузка…
Ссылка в новой задаче