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:
Kelsey Gilbert 2024-04-02 20:36:25 +00:00
Родитель f80f8a5040
Коммит 2bfd7faf83
12 изменённых файлов: 190 добавлений и 49 удалений

Просмотреть файл

@ -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;