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() { (void)Send__delete__(this); }
20 void WebGLChild::ActorDestroy(ActorDestroyReason why
) {
21 mPendingCmdsShmem
= {};
26 Maybe
<Range
<uint8_t>> WebGLChild::AllocPendingCmdBytes(
27 const size_t size
, const size_t fyiAlignmentOverhead
) {
28 if (!mPendingCmdsShmem
.Size()) {
29 size_t capacity
= mDefaultCmdsShmemSize
;
30 if (capacity
< size
) {
34 mPendingCmdsShmem
= mozilla::ipc::BigBuffer::TryAlloc(capacity
);
35 if (!mPendingCmdsShmem
.Size()) {
36 NS_WARNING("Failed to alloc shmem for AllocPendingCmdBytes.");
40 mPendingCmdsAlignmentOverhead
= 0;
43 const auto ptr
= mPendingCmdsShmem
.Data();
44 const auto initialOffset
= AlignmentOffset(kUniversalAlignment
, ptr
);
45 MOZ_ALWAYS_TRUE(!initialOffset
);
49 const auto range
= Range
<uint8_t>{mPendingCmdsShmem
.AsSpan()};
51 auto itr
= range
.begin() + mPendingCmdsPos
;
52 const auto offset
= AlignmentOffset(kUniversalAlignment
, itr
.get());
53 mPendingCmdsPos
+= offset
;
54 mPendingCmdsAlignmentOverhead
+= offset
;
55 const auto required
= mPendingCmdsPos
+ size
;
56 if (required
> range
.length()) {
58 return AllocPendingCmdBytes(size
, fyiAlignmentOverhead
);
60 itr
= range
.begin() + mPendingCmdsPos
;
61 const auto remaining
= Range
<uint8_t>{itr
, range
.end()};
62 mPendingCmdsPos
+= size
;
63 mPendingCmdsAlignmentOverhead
+= fyiAlignmentOverhead
;
64 return Some(Range
<uint8_t>{remaining
.begin(), remaining
.begin() + size
});
67 void WebGLChild::FlushPendingCmds() {
68 if (!mPendingCmdsShmem
.Size()) return;
70 const auto byteSize
= mPendingCmdsPos
;
71 SendDispatchCommands(std::move(mPendingCmdsShmem
), byteSize
);
72 mPendingCmdsShmem
= {};
74 mFlushedCmdInfo
.flushes
+= 1;
75 mFlushedCmdInfo
.flushedCmdBytes
+= byteSize
;
76 mFlushedCmdInfo
.overhead
+= mPendingCmdsAlignmentOverhead
;
77 if (mFlushedCmdInfo
.flushesSinceLastCongestionCheck
.isSome()) {
78 mFlushedCmdInfo
.flushesSinceLastCongestionCheck
.ref() += 1;
79 const auto startCongestionCheck
= 20;
80 const auto maybeIPCMessageCongestion
= 70;
81 const auto eventTarget
= GetCurrentSerialEventTarget();
82 MOZ_ASSERT(eventTarget
);
83 RefPtr
<WebGLChild
> self
= this;
84 size_t generation
= self
->mFlushedCmdInfo
.congestionCheckGeneration
;
86 // When ClientWebGLContext uses async remote texture, sync GetFrontBuffer
87 // message is not sent in ClientWebGLContext::GetFrontBuffer(). It causes a
88 // case that a lot of async DispatchCommands messages are sent to
89 // WebGLParent without calling ClientWebGLContext::GetFrontBuffer(). The
90 // sending DispatchCommands messages could be faster than receiving message
91 // at WebGLParent by WebGLParent::RecvDispatchCommands(). If it happens,
92 // pending IPC messages could grow too much until out of resource. To detect
93 // the messages congestion, async Ping message is used. If the Ping response
94 // is not received until maybeIPCMessageCongestion, IPC message might be
95 // congested at WebGLParent. Then sending sync SyncPing flushes all pending
97 // Due to the async nature of the async ping, it is possible for the flush
98 // check to exceed maybeIPCMessageCongestion, but that it it still bounded.
99 if (mFlushedCmdInfo
.flushesSinceLastCongestionCheck
.ref() ==
100 startCongestionCheck
) {
101 SendPing()->Then(eventTarget
, __func__
, [self
, generation
]() {
102 if (generation
== self
->mFlushedCmdInfo
.congestionCheckGeneration
) {
103 // Confirmed IPC messages congestion does not happen.
104 // Reset flushesSinceLastCongestionCheck for next congestion check.
105 self
->mFlushedCmdInfo
.flushesSinceLastCongestionCheck
= Some(0);
106 self
->mFlushedCmdInfo
.congestionCheckGeneration
++;
109 } else if (mFlushedCmdInfo
.flushesSinceLastCongestionCheck
.ref() >
110 maybeIPCMessageCongestion
) {
111 // IPC messages congestion might happen, send sync SyncPing for flushing
114 // Reset flushesSinceLastCongestionCheck for next congestion check.
115 mFlushedCmdInfo
.flushesSinceLastCongestionCheck
= Some(0);
116 mFlushedCmdInfo
.congestionCheckGeneration
++;
120 if (gl::GLContext::ShouldSpew()) {
121 const auto overheadRatio
= float(mPendingCmdsAlignmentOverhead
) /
122 (byteSize
- mPendingCmdsAlignmentOverhead
);
123 const auto totalOverheadRatio
=
124 float(mFlushedCmdInfo
.overhead
) /
125 (mFlushedCmdInfo
.flushedCmdBytes
- mFlushedCmdInfo
.overhead
);
127 "[WebGLChild] Flushed %zu (%zu=%.2f%% overhead) bytes."
128 " (%zu (%.2f%% overhead) over %zu flushes)\n",
129 byteSize
, mPendingCmdsAlignmentOverhead
, 100 * overheadRatio
,
130 mFlushedCmdInfo
.flushedCmdBytes
, 100 * totalOverheadRatio
,
131 mFlushedCmdInfo
.flushes
);
137 mozilla::ipc::IPCResult
WebGLChild::RecvJsWarning(
138 const std::string
& text
) const {
139 if (!mContext
) return IPC_OK();
140 mContext
->JsWarning(text
);
144 mozilla::ipc::IPCResult
WebGLChild::RecvOnContextLoss(
145 const webgl::ContextLossReason reason
) const {
146 if (!mContext
) return IPC_OK();
147 mContext
->OnContextLoss(reason
);
151 } // namespace mozilla::dom