1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
/* -*- 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 "WebGLMethodDispatcher.h"
#include "mozilla/StaticPrefs_webgl.h"
namespace mozilla::dom {
WebGLChild::WebGLChild(ClientWebGLContext& context)
: mContext(&context),
mDefaultCmdsShmemSize(StaticPrefs::webgl_out_of_process_shmem_size()) {}
WebGLChild::~WebGLChild() { Destroy(); }
void WebGLChild::Destroy() {
if (!CanSend()) {
return;
}
if (mContext) {
mContext->OnDestroyChild(this);
}
(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;
// Handle flushesSinceLastCongestionCheck
mFlushedCmdInfo.flushesSinceLastCongestionCheck += 1;
constexpr auto START_CONGESTION_CHECK_THRESHOLD = 20;
constexpr auto ASSUME_IPC_CONGESTION_THRESHOLD = 70;
RefPtr<WebGLChild> self = this;
size_t generation = self->mFlushedCmdInfo.congestionCheckGeneration;
// When ClientWebGLContext uses async remote texture, sync GetFrontBuffer
// message is not sent in ClientWebGLContext::GetFrontBuffer(). It causes a
// case that a lot of async DispatchCommands messages are sent to
// WebGLParent without calling ClientWebGLContext::GetFrontBuffer(). The
// sending DispatchCommands messages could be faster than receiving message
// at WebGLParent by WebGLParent::RecvDispatchCommands(). If it happens,
// pending IPC messages could grow too much until out of resource. To detect
// the messages congestion, async Ping message is used. If the Ping response
// is not received until maybeIPCMessageCongestion, IPC message might be
// congested at WebGLParent. Then sending sync SyncPing flushes all pending
// messages.
// Due to the async nature of the async ping, it is possible for the flush
// check to exceed maybeIPCMessageCongestion, but that it it still bounded.
if (mFlushedCmdInfo.flushesSinceLastCongestionCheck ==
START_CONGESTION_CHECK_THRESHOLD) {
const auto eventTarget = RefPtr{GetCurrentSerialEventTarget()};
MOZ_ASSERT(eventTarget);
if (!eventTarget) {
NS_WARNING("GetCurrentSerialEventTarget()->nullptr in FlushPendingCmds.");
} else {
SendPing()->Then(eventTarget, __func__, [self, generation]() {
if (generation == self->mFlushedCmdInfo.congestionCheckGeneration) {
// Confirmed IPC messages congestion does not happen.
// Reset flushesSinceLastCongestionCheck for next congestion check.
self->mFlushedCmdInfo.flushesSinceLastCongestionCheck = 0;
self->mFlushedCmdInfo.congestionCheckGeneration++;
}
});
}
} else if (mFlushedCmdInfo.flushesSinceLastCongestionCheck >
ASSUME_IPC_CONGESTION_THRESHOLD) {
// IPC messages congestion might happen, send sync SyncPing for flushing
// pending messages.
SendSyncPing();
// Reset flushesSinceLastCongestionCheck for next congestion check.
mFlushedCmdInfo.flushesSinceLastCongestionCheck = 0;
mFlushedCmdInfo.congestionCheckGeneration++;
}
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();
}
mozilla::ipc::IPCResult WebGLChild::RecvOnSyncComplete(
const webgl::ObjectId id) const {
if (!mContext) return IPC_OK();
mContext->OnSyncComplete(id);
return IPC_OK();
}
} // namespace mozilla::dom
|