1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "WebGLChild.h"
8 #include "ClientWebGLContext.h"
9 #include "mozilla/StaticPrefs_webgl.h"
10 #include "WebGLMethodDispatcher.h"
12 namespace mozilla::dom
{
14 WebGLChild::WebGLChild(ClientWebGLContext
& context
)
16 mDefaultCmdsShmemSize(StaticPrefs::webgl_out_of_process_shmem_size()) {}
18 WebGLChild::~WebGLChild() { Destroy(); }
20 void WebGLChild::Destroy() {
25 mContext
->OnDestroyChild(this);
27 (void)Send__delete__(this);
30 void WebGLChild::ActorDestroy(ActorDestroyReason why
) {
31 mPendingCmdsShmem
= {};
36 Maybe
<Range
<uint8_t>> WebGLChild::AllocPendingCmdBytes(
37 const size_t size
, const size_t fyiAlignmentOverhead
) {
38 if (!mPendingCmdsShmem
.Size()) {
39 size_t capacity
= mDefaultCmdsShmemSize
;
40 if (capacity
< size
) {
44 mPendingCmdsShmem
= mozilla::ipc::BigBuffer::TryAlloc(capacity
);
45 if (!mPendingCmdsShmem
.Size()) {
46 NS_WARNING("Failed to alloc shmem for AllocPendingCmdBytes.");
50 mPendingCmdsAlignmentOverhead
= 0;
53 const auto ptr
= mPendingCmdsShmem
.Data();
54 const auto initialOffset
= AlignmentOffset(kUniversalAlignment
, ptr
);
55 MOZ_ALWAYS_TRUE(!initialOffset
);
59 const auto range
= Range
<uint8_t>{mPendingCmdsShmem
.AsSpan()};
61 auto itr
= range
.begin() + mPendingCmdsPos
;
62 const auto offset
= AlignmentOffset(kUniversalAlignment
, itr
.get());
63 mPendingCmdsPos
+= offset
;
64 mPendingCmdsAlignmentOverhead
+= offset
;
65 const auto required
= mPendingCmdsPos
+ size
;
66 if (required
> range
.length()) {
68 return AllocPendingCmdBytes(size
, fyiAlignmentOverhead
);
70 itr
= range
.begin() + mPendingCmdsPos
;
71 const auto remaining
= Range
<uint8_t>{itr
, range
.end()};
72 mPendingCmdsPos
+= size
;
73 mPendingCmdsAlignmentOverhead
+= fyiAlignmentOverhead
;
74 return Some(Range
<uint8_t>{remaining
.begin(), remaining
.begin() + size
});
77 void WebGLChild::FlushPendingCmds() {
78 if (!mPendingCmdsShmem
.Size()) return;
80 const auto byteSize
= mPendingCmdsPos
;
81 SendDispatchCommands(std::move(mPendingCmdsShmem
), byteSize
);
82 mPendingCmdsShmem
= {};
84 mFlushedCmdInfo
.flushes
+= 1;
85 mFlushedCmdInfo
.flushedCmdBytes
+= byteSize
;
86 mFlushedCmdInfo
.overhead
+= mPendingCmdsAlignmentOverhead
;
88 // Handle flushesSinceLastCongestionCheck
89 mFlushedCmdInfo
.flushesSinceLastCongestionCheck
+= 1;
90 constexpr auto START_CONGESTION_CHECK_THRESHOLD
= 20;
91 constexpr auto ASSUME_IPC_CONGESTION_THRESHOLD
= 70;
92 RefPtr
<WebGLChild
> self
= this;
93 size_t generation
= self
->mFlushedCmdInfo
.congestionCheckGeneration
;
95 // When ClientWebGLContext uses async remote texture, sync GetFrontBuffer
96 // message is not sent in ClientWebGLContext::GetFrontBuffer(). It causes a
97 // case that a lot of async DispatchCommands messages are sent to
98 // WebGLParent without calling ClientWebGLContext::GetFrontBuffer(). The
99 // sending DispatchCommands messages could be faster than receiving message
100 // at WebGLParent by WebGLParent::RecvDispatchCommands(). If it happens,
101 // pending IPC messages could grow too much until out of resource. To detect
102 // the messages congestion, async Ping message is used. If the Ping response
103 // is not received until maybeIPCMessageCongestion, IPC message might be
104 // congested at WebGLParent. Then sending sync SyncPing flushes all pending
106 // Due to the async nature of the async ping, it is possible for the flush
107 // check to exceed maybeIPCMessageCongestion, but that it it still bounded.
108 if (mFlushedCmdInfo
.flushesSinceLastCongestionCheck
==
109 START_CONGESTION_CHECK_THRESHOLD
) {
110 const auto eventTarget
= RefPtr
{GetCurrentSerialEventTarget()};
111 MOZ_ASSERT(eventTarget
);
113 NS_WARNING("GetCurrentSerialEventTarget()->nullptr in FlushPendingCmds.");
115 SendPing()->Then(eventTarget
, __func__
, [self
, generation
]() {
116 if (generation
== self
->mFlushedCmdInfo
.congestionCheckGeneration
) {
117 // Confirmed IPC messages congestion does not happen.
118 // Reset flushesSinceLastCongestionCheck for next congestion check.
119 self
->mFlushedCmdInfo
.flushesSinceLastCongestionCheck
= 0;
120 self
->mFlushedCmdInfo
.congestionCheckGeneration
++;
124 } else if (mFlushedCmdInfo
.flushesSinceLastCongestionCheck
>
125 ASSUME_IPC_CONGESTION_THRESHOLD
) {
126 // IPC messages congestion might happen, send sync SyncPing for flushing
129 // Reset flushesSinceLastCongestionCheck for next congestion check.
130 mFlushedCmdInfo
.flushesSinceLastCongestionCheck
= 0;
131 mFlushedCmdInfo
.congestionCheckGeneration
++;
134 if (gl::GLContext::ShouldSpew()) {
135 const auto overheadRatio
= float(mPendingCmdsAlignmentOverhead
) /
136 (byteSize
- mPendingCmdsAlignmentOverhead
);
137 const auto totalOverheadRatio
=
138 float(mFlushedCmdInfo
.overhead
) /
139 (mFlushedCmdInfo
.flushedCmdBytes
- mFlushedCmdInfo
.overhead
);
141 "[WebGLChild] Flushed %zu (%zu=%.2f%% overhead) bytes."
142 " (%zu (%.2f%% overhead) over %zu flushes)\n",
143 byteSize
, mPendingCmdsAlignmentOverhead
, 100 * overheadRatio
,
144 mFlushedCmdInfo
.flushedCmdBytes
, 100 * totalOverheadRatio
,
145 mFlushedCmdInfo
.flushes
);
151 mozilla::ipc::IPCResult
WebGLChild::RecvJsWarning(
152 const std::string
& text
) const {
153 if (!mContext
) return IPC_OK();
154 mContext
->JsWarning(text
);
158 mozilla::ipc::IPCResult
WebGLChild::RecvOnContextLoss(
159 const webgl::ContextLossReason reason
) const {
160 if (!mContext
) return IPC_OK();
161 mContext
->OnContextLoss(reason
);
165 mozilla::ipc::IPCResult
WebGLChild::RecvOnSyncComplete(
166 const webgl::ObjectId id
) const {
167 if (!mContext
) return IPC_OK();
168 mContext
->OnSyncComplete(id
);
172 } // namespace mozilla::dom