fune/dom/canvas/WebGLChild.cpp
Kelsey Gilbert 7671a9d5a4 Bug 1801021 - Use BigBuffer for DispatchCommands. r=gfx-reviewers,lsalzman
Using ipc::Shmem causes unbounded shmem use growth until e.g. a Worker
yields to the event loop. If a Worker never yields, Shmems sent to WebGLParent
are never released.

Specifically the manager (PCanvasManager) for WebGLParent calls
DestroySharedMemory, which sends/enqueues for WebGLChild's manager a
matching call to ShmemDestroyed. However, while WebGLChild refuses to spin its
event loop (such as a no-return WASM Worker), the ShmemDestroyed events
will just pile up. Closing e.g. the tab frees the shmems, but they accumulate
unbounded until the Worker yields to the event loop.

This is true for other users of ipc::Shmem (or RaiiShmem) as well, but
entrypoints other than DispatchCommands are rarer and can be handled
later similarly.

Differential Revision: https://phabricator.services.mozilla.com/D162946
2022-11-25 22:20:38 +00:00

109 lines
3.5 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "WebGLChild.h"
#include "ClientWebGLContext.h"
#include "mozilla/StaticPrefs_webgl.h"
#include "WebGLMethodDispatcher.h"
namespace mozilla::dom {
WebGLChild::WebGLChild(ClientWebGLContext& context)
: mContext(&context),
mDefaultCmdsShmemSize(StaticPrefs::webgl_out_of_process_shmem_size()) {}
WebGLChild::~WebGLChild() { (void)Send__delete__(this); }
void WebGLChild::ActorDestroy(ActorDestroyReason why) {
mPendingCmdsShmem = {};
}
// -
Maybe<Range<uint8_t>> WebGLChild::AllocPendingCmdBytes(
const size_t size, const size_t fyiAlignmentOverhead) {
if (!mPendingCmdsShmem.Size()) {
size_t capacity = mDefaultCmdsShmemSize;
if (capacity < size) {
capacity = size;
}
mPendingCmdsShmem = mozilla::ipc::BigBuffer::TryAlloc(capacity);
if (!mPendingCmdsShmem.Size()) {
NS_WARNING("Failed to alloc shmem for AllocPendingCmdBytes.");
return {};
}
mPendingCmdsPos = 0;
mPendingCmdsAlignmentOverhead = 0;
if (kIsDebug) {
const auto ptr = mPendingCmdsShmem.Data();
const auto initialOffset = AlignmentOffset(kUniversalAlignment, ptr);
MOZ_ALWAYS_TRUE(!initialOffset);
}
}
const auto range = Range<uint8_t>{mPendingCmdsShmem.AsSpan()};
auto itr = range.begin() + mPendingCmdsPos;
const auto offset = AlignmentOffset(kUniversalAlignment, itr.get());
mPendingCmdsPos += offset;
mPendingCmdsAlignmentOverhead += offset;
const auto required = mPendingCmdsPos + size;
if (required > range.length()) {
FlushPendingCmds();
return AllocPendingCmdBytes(size, fyiAlignmentOverhead);
}
itr = range.begin() + mPendingCmdsPos;
const auto remaining = Range<uint8_t>{itr, range.end()};
mPendingCmdsPos += size;
mPendingCmdsAlignmentOverhead += fyiAlignmentOverhead;
return Some(Range<uint8_t>{remaining.begin(), remaining.begin() + size});
}
void WebGLChild::FlushPendingCmds() {
if (!mPendingCmdsShmem.Size()) return;
const auto byteSize = mPendingCmdsPos;
SendDispatchCommands(std::move(mPendingCmdsShmem), byteSize);
mPendingCmdsShmem = {};
mFlushedCmdInfo.flushes += 1;
mFlushedCmdInfo.flushedCmdBytes += byteSize;
mFlushedCmdInfo.overhead += mPendingCmdsAlignmentOverhead;
if (gl::GLContext::ShouldSpew()) {
const auto overheadRatio = float(mPendingCmdsAlignmentOverhead) /
(byteSize - mPendingCmdsAlignmentOverhead);
const auto totalOverheadRatio =
float(mFlushedCmdInfo.overhead) /
(mFlushedCmdInfo.flushedCmdBytes - mFlushedCmdInfo.overhead);
printf_stderr(
"[WebGLChild] Flushed %zu (%zu=%.2f%% overhead) bytes."
" (%zu (%.2f%% overhead) over %zu flushes)\n",
byteSize, mPendingCmdsAlignmentOverhead, 100 * overheadRatio,
mFlushedCmdInfo.flushedCmdBytes, 100 * totalOverheadRatio,
mFlushedCmdInfo.flushes);
}
}
// -
mozilla::ipc::IPCResult WebGLChild::RecvJsWarning(
const std::string& text) const {
if (!mContext) return IPC_OK();
mContext->JsWarning(text);
return IPC_OK();
}
mozilla::ipc::IPCResult WebGLChild::RecvOnContextLoss(
const webgl::ContextLossReason reason) const {
if (!mContext) return IPC_OK();
mContext->OnContextLoss(reason);
return IPC_OK();
}
} // namespace mozilla::dom