WebGLChild.cpp (6301B)
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/. */ 5 6 #include "WebGLChild.h" 7 8 #include "ClientWebGLContext.h" 9 #include "WebGLMethodDispatcher.h" 10 #include "mozilla/StaticPrefs_webgl.h" 11 12 namespace mozilla::dom { 13 14 WebGLChild::WebGLChild(ClientWebGLContext& context) 15 : mContext(&context), 16 mDefaultCmdsShmemSize(StaticPrefs::webgl_out_of_process_shmem_size()) {} 17 18 WebGLChild::~WebGLChild() { Destroy(); } 19 20 void WebGLChild::Destroy() { 21 if (!CanSend()) { 22 return; 23 } 24 if (mContext) { 25 mContext->OnDestroyChild(this); 26 } 27 (void)Send__delete__(this); 28 } 29 30 void WebGLChild::ActorDestroy(ActorDestroyReason why) { 31 mPendingCmdsShmem = {}; 32 } 33 34 // - 35 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) { 41 capacity = size; 42 } 43 44 mPendingCmdsShmem = mozilla::ipc::BigBuffer::TryAlloc(capacity); 45 if (!mPendingCmdsShmem.Size()) { 46 NS_WARNING("Failed to alloc shmem for AllocPendingCmdBytes."); 47 return {}; 48 } 49 mPendingCmdsPos = 0; 50 mPendingCmdsAlignmentOverhead = 0; 51 52 if (kIsDebug) { 53 const auto ptr = mPendingCmdsShmem.Data(); 54 const auto initialOffset = AlignmentOffset(kUniversalAlignment, ptr); 55 MOZ_ALWAYS_TRUE(!initialOffset); 56 } 57 } 58 59 const auto range = Range<uint8_t>{mPendingCmdsShmem.AsSpan()}; 60 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()) { 67 FlushPendingCmds(); 68 return AllocPendingCmdBytes(size, fyiAlignmentOverhead); 69 } 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}); 75 } 76 77 void WebGLChild::FlushPendingCmds() { 78 if (!mPendingCmdsShmem.Size()) return; 79 80 const auto byteSize = mPendingCmdsPos; 81 SendDispatchCommands(std::move(mPendingCmdsShmem), byteSize); 82 mPendingCmdsShmem = {}; 83 84 mFlushedCmdInfo.flushes += 1; 85 mFlushedCmdInfo.flushedCmdBytes += byteSize; 86 mFlushedCmdInfo.overhead += mPendingCmdsAlignmentOverhead; 87 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; 94 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 105 // messages. 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); 112 if (!eventTarget) { 113 NS_WARNING("GetCurrentSerialEventTarget()->nullptr in FlushPendingCmds."); 114 } else { 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++; 121 } 122 }); 123 } 124 } else if (mFlushedCmdInfo.flushesSinceLastCongestionCheck > 125 ASSUME_IPC_CONGESTION_THRESHOLD) { 126 // IPC messages congestion might happen, send sync SyncPing for flushing 127 // pending messages. 128 SendSyncPing(); 129 // Reset flushesSinceLastCongestionCheck for next congestion check. 130 mFlushedCmdInfo.flushesSinceLastCongestionCheck = 0; 131 mFlushedCmdInfo.congestionCheckGeneration++; 132 } 133 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); 140 printf_stderr( 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); 146 } 147 } 148 149 // - 150 151 mozilla::ipc::IPCResult WebGLChild::RecvJsWarning( 152 const std::string& text) const { 153 if (!mContext) return IPC_OK(); 154 mContext->JsWarning(text); 155 return IPC_OK(); 156 } 157 158 mozilla::ipc::IPCResult WebGLChild::RecvOnContextLoss( 159 const webgl::ContextLossReason reason) const { 160 if (!mContext) return IPC_OK(); 161 mContext->OnContextLoss(reason); 162 return IPC_OK(); 163 } 164 165 mozilla::ipc::IPCResult WebGLChild::RecvOnSyncComplete( 166 const webgl::ObjectId id) const { 167 if (!mContext) return IPC_OK(); 168 mContext->OnSyncComplete(id); 169 return IPC_OK(); 170 } 171 172 } // namespace mozilla::dom