diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index ef5b6b025ed9..de1b6c45411e 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -105,20 +105,6 @@ ClientWebGLContext::ClientWebGLContext(const bool webgl2) ClientWebGLContext::~ClientWebGLContext() { RemovePostRefreshObserver(); } -bool ClientWebGLContext::UpdateCompositableHandle( - LayerTransactionChild* aLayerTransaction, CompositableHandle aHandle) { - // When running OOP WebGL (i.e. when we have a WebGLChild actor), tell the - // host about the new compositable. When running in-process, we don't need to - // care. - if (mNotLost->outOfProcess) { - WEBGL_BRIDGE_LOGI("[%p] Setting CompositableHandle to %" PRIx64, this, - aHandle.Value()); - return mNotLost->outOfProcess->mWebGLChild->SendUpdateCompositableHandle( - aLayerTransaction, aHandle); - } - return true; -} - void ClientWebGLContext::JsWarning(const std::string& utf8) const { if (!mCanvasElement) { return; @@ -160,7 +146,7 @@ bool ClientWebGLContext::DispatchEvent(const nsAString& eventName) const { // - -void ClientWebGLContext::EmulateLoseContext() { +void ClientWebGLContext::EmulateLoseContext() const { const FuncScope funcScope(*this, "loseContext"); if (mLossStatus != webgl::LossStatus::Ready) { JsWarning("loseContext: Already lost."); @@ -172,7 +158,8 @@ void ClientWebGLContext::EmulateLoseContext() { OnContextLoss(webgl::ContextLossReason::Manual); } -void ClientWebGLContext::OnContextLoss(const webgl::ContextLossReason reason) { +void ClientWebGLContext::OnContextLoss( + const webgl::ContextLossReason reason) const { MOZ_ASSERT(NS_IsMainThread()); JsWarning("WebGL context was lost."); @@ -199,9 +186,9 @@ void ClientWebGLContext::OnContextLoss(const webgl::ContextLossReason reason) { break; } - const auto weak = WeakPtr(this); + const auto weak = WeakPtr(this); const auto fnRun = [weak]() { - const auto strong = RefPtr(weak); + const auto strong = RefPtr(weak); if (!strong) return; strong->Event_webglcontextlost(); }; @@ -210,7 +197,7 @@ void ClientWebGLContext::OnContextLoss(const webgl::ContextLossReason reason) { NS_DispatchToCurrentThread(std::move(runnable)); } -void ClientWebGLContext::Event_webglcontextlost() { +void ClientWebGLContext::Event_webglcontextlost() const { WEBGL_BRIDGE_LOGD("[%p] Posting webglcontextlost event", this); const bool useDefaultHandler = DispatchEvent(u"webglcontextlost"_ns); if (useDefaultHandler) { @@ -223,7 +210,7 @@ void ClientWebGLContext::Event_webglcontextlost() { } void ClientWebGLContext::RestoreContext( - const webgl::LossStatus requiredStatus) { + const webgl::LossStatus requiredStatus) const { if (requiredStatus != mLossStatus) { JsWarning( "restoreContext: Only valid iff context lost with loseContext()."); @@ -238,9 +225,9 @@ void ClientWebGLContext::RestoreContext( if (mAwaitingRestore) return; mAwaitingRestore = true; - const auto weak = WeakPtr(this); + const auto weak = WeakPtr(this); const auto fnRun = [weak]() { - const auto strong = RefPtr(weak); + const auto strong = RefPtr(weak); if (!strong) return; strong->Event_webglcontextrestored(); }; @@ -249,13 +236,15 @@ void ClientWebGLContext::RestoreContext( NS_DispatchToCurrentThread(std::move(runnable)); } -void ClientWebGLContext::Event_webglcontextrestored() { +void ClientWebGLContext::Event_webglcontextrestored() const { mAwaitingRestore = false; mLossStatus = webgl::LossStatus::Ready; mNextError = 0; const uvec2 requestSize = {mCanvasElement->Width(), mCanvasElement->Height()}; - if (!CreateHostContext(requestSize)) { + const auto mutThis = const_cast( + this); // TODO: Make context loss non-mutable. + if (!mutThis->CreateHostContext(requestSize)) { mLossStatus = webgl::LossStatus::LostForever; return; } @@ -293,99 +282,34 @@ void ClientWebGLContext::ThrowEvent_WebGLContextCreationError( target->DispatchEvent(*event); } -// --- -/* -// Dispatch a command to the host, using data in WebGLMethodDispatcher for -// information: e.g. to choose the right synchronization protocol. -template -struct WebGLClientDispatcher { - // non-const method - template - static ReturnType Run(const ClientWebGLContext& c, - ReturnType (HostWebGLContext::*method)(MethodArgs...), - GivenArgs&&... aArgs) { - // Non-void calls must be sync, otherwise what would we return? - MOZ_ASSERT(WebGLMethodDispatcher::SyncType() == CommandSyncType::SYNC); - return c.DispatchSync( - static_cast(aArgs)...); - } - - // const method - template - static ReturnType Run(const ClientWebGLContext& c, - ReturnType (HostWebGLContext::*method)(MethodArgs...) - const, - GivenArgs&&... aArgs) { - // Non-void calls must be sync, otherwise what would we return? - MOZ_ASSERT(WebGLMethodDispatcher::SyncType() == CommandSyncType::SYNC); - return c.DispatchSync( - static_cast(aArgs)...); - } -}; - -template <> -struct WebGLClientDispatcher { - // non-const method - template - static void Run(const ClientWebGLContext& c, - void (HostWebGLContext::*method)(MethodArgs...), - GivenArgs&&... aArgs) { - if (WebGLMethodDispatcher::SyncType() == CommandSyncType::SYNC) { - c.DispatchVoidSync(static_cast(aArgs)...); - } else { - c.DispatchAsync(static_cast(aArgs)...); - } - } - - // const method - template - static void Run(const ClientWebGLContext& c, - void (HostWebGLContext::*method)(MethodArgs...) const, - GivenArgs&&... aArgs) { - if (WebGLMethodDispatcher::SyncType() == CommandSyncType::SYNC) { - c.DispatchVoidSync(static_cast(aArgs)...); - } else { - c.DispatchAsync(static_cast(aArgs)...); - } - } -}; -*/ -template -inline T DefaultOrVoid() { - return {}; -} - -template <> -inline void DefaultOrVoid() { - return; -} - -template -ReturnType RunOn(const ClientWebGLContext& context, Args&&... aArgs) { - const auto notLost = - context.mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return DefaultOrVoid(); - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - return ((inProcessContext.get())->*method)(std::forward(aArgs)...); - } - MOZ_CRASH("todo"); - // return WebGLClientDispatcher::template Run(*this, method, - // aArgs...); -} +// - // If we are running WebGL in this process then call the HostWebGLContext // method directly. Otherwise, dispatch over IPC. -template -// template < -// typename MethodType, MethodType method, -// typename ReturnType, size_t Id, -// typename... Args> -ReturnType ClientWebGLContext::Run(Args&&... aArgs) const { - return RunOn( - *this, std::forward(aArgs)...); +template +void ClientWebGLContext::Run(Args&&... args) const { + const auto notLost = + mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. + if (IsContextLost()) return; + + const auto& inProcess = notLost->inProcess; + if (inProcess) { + return (inProcess.get()->*method)(std::forward(args)...); + } + + const auto& child = notLost->outOfProcess->mWebGLChild; + + const auto id = IdByMethod(); + + const auto size = webgl::SerializedSize(id, args...); + const auto maybeDest = child->AllocPendingCmdBytes(size); + if (!maybeDest) { + JsWarning("Failed to allocate internal command buffer."); + OnContextLoss(webgl::ContextLossReason::None); + return; + } + const auto& destBytes = *maybeDest; + webgl::Serialize(destBytes, id, args...); } // ------------------------------------------------------------------------- @@ -425,7 +349,19 @@ void ClientWebGLContext::Present(WebGLFramebufferJS* const xrFb, Maybe ClientWebGLContext::GetFrontBuffer( WebGLFramebufferJS* const fb, bool vr) { - return Run(fb ? fb->mId : 0, vr); + const auto notLost = mNotLost; + if (IsContextLost()) return {}; + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetFrontBuffer(fb ? fb->mId : 0, vr); + } + + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetFrontBuffer(fb ? fb->mId : 0, vr, &ret)) return {}; + return ret; } void ClientWebGLContext::ClearVRSwapChain() { Run(); } @@ -638,7 +574,12 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { // - - if (!StaticPrefs::webgl_out_of_process()) { + auto useOop = StaticPrefs::webgl_out_of_process(); + if (XRE_IsParentProcess()) { + useOop = false; + } + + if (!useOop) { auto ownerData = HostWebGLContext::OwnerData{ Some(this), }; @@ -666,39 +607,36 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { UniquePtr sinkP; UniquePtr sinkI; - switch (StaticPrefs::webgl_prototype_ipc_pcq()) { - case 0: { - using mozilla::webgl::ProducerConsumerQueue; - static constexpr size_t CommandQueueSize = 256 * 1024; // 256K - static constexpr size_t ResponseQueueSize = 8 * 1024; // 8K - auto command = ProducerConsumerQueue::Create(cbc, CommandQueueSize); - auto response = ProducerConsumerQueue::Create(cbc, ResponseQueueSize); - if (!command || !response) { - return Err("Failed to create command/response PCQ"); - } - - outOfProcess.mCommandSourcePcq = MakeUnique( - command->TakeProducer(), response->TakeConsumer()); - sinkP = MakeUnique(command->TakeConsumer(), - response->TakeProducer()); - break; + if (StaticPrefs::webgl_oop_via_pcq()) { + using mozilla::webgl::ProducerConsumerQueue; + static constexpr size_t CommandQueueSize = 256 * 1024; // 256K + static constexpr size_t ResponseQueueSize = 8 * 1024; // 8K + auto command = ProducerConsumerQueue::Create(cbc, CommandQueueSize); + auto response = ProducerConsumerQueue::Create(cbc, ResponseQueueSize); + if (!command || !response) { + return Err("Failed to create command/response PCQ"); } - default: - using mozilla::IpdlWebGLCommandQueue; - using mozilla::IpdlWebGLResponseQueue; - auto command = - IpdlWebGLCommandQueue::Create(outOfProcess.mWebGLChild.get()); - auto response = - IpdlWebGLResponseQueue::Create(outOfProcess.mWebGLChild.get()); - if (!command || !response) { - return Err("Failed to create command/response IpdlQueue"); - } - outOfProcess.mCommandSourceIpdl = MakeUnique( - command->TakeProducer(), response->TakeConsumer()); - sinkI = MakeUnique(command->TakeConsumer(), - response->TakeProducer()); - break; + outOfProcess.mCommandSourcePcq = MakeUnique( + command->TakeProducer(), response->TakeConsumer()); + sinkP = MakeUnique(command->TakeConsumer(), + response->TakeProducer()); + + } else { + using mozilla::IpdlWebGLCommandQueue; + using mozilla::IpdlWebGLResponseQueue; + auto command = + IpdlWebGLCommandQueue::Create(outOfProcess.mWebGLChild.get()); + auto response = + IpdlWebGLResponseQueue::Create(outOfProcess.mWebGLChild.get()); + if (!command || !response) { + return Err("Failed to create command/response IpdlQueue"); + } + + outOfProcess.mCommandSourceIpdl = MakeUnique( + command->TakeProducer(), response->TakeConsumer()); + sinkI = MakeUnique(command->TakeConsumer(), + response->TakeProducer()); } if (!outOfProcess.mWebGLChild->SendInitialize( @@ -770,16 +708,32 @@ uvec2 ClientWebGLContext::DrawingBufferSize() { if (IsContextLost()) return {}; auto& state = State(); auto& size = state.mDrawingBufferSize; + if (!size) { - size = Some(Run()); + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + size = Some(inProcess->DrawingBufferSize()); + } else { + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + uvec2 actual = {}; + if (!child->SendDrawingBufferSize(&actual)) return {}; + size = Some(actual); + } } return *size; } void ClientWebGLContext::OnMemoryPressure() { - WEBGL_BRIDGE_LOGI("[%p] OnMemoryPressure", this); - return Run(); + if (IsContextLost()) return; + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->OnMemoryPressure(); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + (void)child->SendOnMemoryPressure(); } NS_IMETHODIMP @@ -890,7 +844,67 @@ RefPtr ClientWebGLContext::GetFrontBufferSnapshot( const auto& options = mNotLost->info.options; - auto snapshot = Run(); + const auto surfFormat = options.alpha ? gfx::SurfaceFormat::B8G8R8A8 + : gfx::SurfaceFormat::B8G8R8X8; + + const auto fnNewSurf = [&](const uvec2 size) { + const auto stride = size.x * 4; + return RefPtr( + gfx::Factory::CreateDataSourceSurfaceWithStride({size.x, size.y}, + surfFormat, stride, + /*zero=*/true)); + }; + + auto snapshot = [&]() -> RefPtr { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + const auto surfSize = inProcess->GetFrontBufferSize(); + const auto stride = surfSize.x * 4; + const auto byteSize = stride * surfSize.y; + const auto surf = fnNewSurf(surfSize); + if (!surf) return nullptr; + { + const gfx::DataSourceSurface::ScopedMap map( + surf, gfx::DataSourceSurface::READ_WRITE); + if (!map.IsMapped()) { + MOZ_ASSERT(false); + return nullptr; + } + MOZ_RELEASE_ASSERT(map.GetStride() == static_cast(stride)); + auto range = Range{map.GetData(), byteSize}; + if (!inProcess->FrontBufferSnapshotInto(range)) return nullptr; + } + return surf; + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + webgl::FrontBufferSnapshotIpc res; + if (!child->SendGetFrontBufferSnapshot(&res)) { + res = {}; + } + if (!res.shmem) return nullptr; + const auto& surfSize = res.surfSize; + const auto& shmemBytes = ByteRange(*res.shmem); + + const auto stride = surfSize.x * 4; + const auto byteSize = stride * surfSize.y; + + const auto surf = fnNewSurf(surfSize); + if (!surf) return nullptr; + + { + const gfx::DataSourceSurface::ScopedMap map( + surf, gfx::DataSourceSurface::READ_WRITE); + if (!map.IsMapped()) { + MOZ_ASSERT(false); + return nullptr; + } + MOZ_RELEASE_ASSERT(map.GetStride() == static_cast(stride)); + MOZ_RELEASE_ASSERT(shmemBytes.length() == byteSize); + memcpy(map.GetData(), shmemBytes.begin().get(), byteSize); + } + return surf; + }(); if (!snapshot) return nullptr; if (requireAlphaPremult && options.alpha && !options.premultipliedAlpha) { @@ -1054,9 +1068,20 @@ ClientWebGLContext::CreateOpaqueFramebuffer( if (IsContextLost()) return nullptr; auto ret = AsRefPtr(new WebGLFramebufferJS(*this, true)); - if (!Run(ret->mId, options)) { - return nullptr; + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + if (!inProcess->CreateOpaqueFramebuffer(ret->mId, options)) { + ret = nullptr; + } + return ret.forget(); } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + bool ok = false; + if (!child->SendCreateOpaqueFramebuffer(ret->mId, options, &ok)) + return nullptr; + if (!ok) return nullptr; return ret.forget(); } @@ -1570,23 +1595,42 @@ void ClientWebGLContext::Disable(GLenum cap) const { Run(cap); } void ClientWebGLContext::Enable(GLenum cap) const { Run(cap); } bool ClientWebGLContext::IsEnabled(GLenum cap) const { - return Run(cap); + const FuncScope funcScope(*this, "isEnabled"); + const auto notLost = mNotLost; + if (IsContextLost()) return false; + + const auto& inProcess = notLost->inProcess; + if (inProcess) { + return inProcess->IsEnabled(cap); + } + const auto& child = notLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + bool ret = {}; + if (!child->SendIsEnabled(cap, &ret)) return false; + return ret; } void ClientWebGLContext::GetInternalformatParameter( JSContext* cx, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandle retval, ErrorResult& rv) { + const FuncScope funcScope(*this, "getInternalformatParameter"); retval.set(JS::NullValue()); const auto notLost = mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return; + if (IsContextLost()) return; + const auto& inProcessContext = notLost->inProcess; Maybe> maybe; if (inProcessContext) { maybe = inProcessContext->GetInternalformatParameter(target, internalformat, pname); } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetInternalformatParameter"); + const auto& child = notLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + if (!child->SendGetInternalformatParameter(target, internalformat, pname, + &maybe)) { + return; + } } if (!maybe) { @@ -1639,6 +1683,42 @@ static JS::Value Create(JSContext* cx, nsWrapperCache* creator, const S& src, return CreateAs(cx, creator, src, rv); } +Maybe ClientWebGLContext::GetNumber(const GLenum pname) { + MOZ_ASSERT(!IsContextLost()); + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetNumber(pname); + } + + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + + Maybe ret; + if (!child->SendGetNumber(pname, &ret)) { + ret.reset(); + } + return ret; +} + +Maybe ClientWebGLContext::GetString(const GLenum pname) { + MOZ_ASSERT(!IsContextLost()); + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetString(pname); + } + + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + + Maybe ret; + if (!child->SendGetString(pname, &ret)) { + ret.reset(); + } + return ret; +} + void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, JS::MutableHandle retval, ErrorResult& rv, const bool debug) { @@ -1927,7 +2007,7 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, return; } - const auto maybe = Run(driverEnum); + const auto maybe = GetString(driverEnum); if (maybe) { retval.set(StringValue(cx, *maybe, rv)); } @@ -1975,12 +2055,12 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, // - if (asString) { - const auto maybe = Run(pname); + const auto maybe = GetString(pname); if (maybe) { retval.set(StringValue(cx, maybe->c_str(), rv)); } } else { - const auto maybe = Run(pname); + const auto maybe = GetNumber(pname); if (maybe) { switch (pname) { // WebGL 1: @@ -2016,7 +2096,21 @@ void ClientWebGLContext::GetBufferParameter( JSContext* cx, GLenum target, GLenum pname, JS::MutableHandle retval) const { retval.set(JS::NullValue()); - const auto maybe = Run(target, pname); + if (IsContextLost()) return; + + const auto maybe = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetBufferParameter(target, pname); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetBufferParameter(target, pname, &ret)) { + ret.reset(); + } + return ret; + }(); if (maybe) { retval.set(JS::NumberValue(*maybe)); } @@ -2055,6 +2149,24 @@ void ClientWebGLContext::GetFramebufferAttachmentParameter( fb = state.mBoundReadFb; } + const auto fnGet = [&](const GLenum pname) { + const auto fbId = fb ? fb->mId : 0; + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetFramebufferAttachmentParameter(fbId, attachment, + pname); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetFramebufferAttachmentParameter(fbId, attachment, pname, + &ret)) { + ret.reset(); + } + return ret; + }; + if (fb) { if (fb->mOpaque) { EnqueueError(LOCAL_GL_INVALID_OPERATION, @@ -2066,8 +2178,7 @@ void ClientWebGLContext::GetFramebufferAttachmentParameter( if (mIsWebGL2 && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { // In webgl2, DEPTH_STENCIL is valid iff the DEPTH and STENCIL images // match, so check if the server errors. - const auto maybe = Run( - fb->mId, attachment, LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + const auto maybe = fnGet(LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); if (!maybe) return; attachmentSlotEnum = LOCAL_GL_DEPTH_ATTACHMENT; } @@ -2095,8 +2206,7 @@ void ClientWebGLContext::GetFramebufferAttachmentParameter( } } - const auto maybe = Run( - fb ? fb->mId : 0, attachment, pname); + const auto maybe = fnGet(pname); if (maybe) { retval.set(JS::NumberValue(*maybe)); } @@ -2116,9 +2226,20 @@ void ClientWebGLContext::GetRenderbufferParameter( const auto& state = State(); const auto& rb = state.mBoundRb; - - const auto maybe = - Run(rb ? rb->mId : 0, pname); + const auto rbId = rb ? rb->mId : 0; + const auto maybe = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetRenderbufferParameter(rbId, pname); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetRenderbufferParameter(rbId, pname, &ret)) { + ret.reset(); + } + return ret; + }(); if (maybe) { retval.set(JS::NumberValue(*maybe)); } @@ -2158,7 +2279,19 @@ void ClientWebGLContext::GetIndexedParameter( } } - const auto maybe = Run(target, index); + const auto maybe = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetIndexedParameter(target, index); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetIndexedParameter(target, index, &ret)) { + ret.reset(); + } + return ret; + }(); if (maybe) { retval.set(JS::NumberValue(*maybe)); } @@ -2186,7 +2319,19 @@ void ClientWebGLContext::GetUniform(JSContext* const cx, return; } - const auto res = Run(prog.mId, loc.mLocation); + const auto res = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetUniform(prog.mId, loc.mLocation); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + webgl::GetUniformData ret; + if (!child->SendGetUniform(prog.mId, loc.mLocation, &ret)) { + ret = {}; + } + return ret; + }(); if (!res.type) return; const auto elemCount = ElemTypeComponents(res.type); @@ -2289,8 +2434,21 @@ void ClientWebGLContext::GetUniform(JSContext* const cx, already_AddRefed ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype, const GLenum precisiontype) { - const auto info = - Run(shadertype, precisiontype); + if (IsContextLost()) return nullptr; + const auto info = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetShaderPrecisionFormat(shadertype, precisiontype); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetShaderPrecisionFormat(shadertype, precisiontype, &ret)) { + ret.reset(); + } + return ret; + }(); + if (!info) return nullptr; return AsAddRefed(new WebGLShaderPrecisionFormatJS(*info)); } @@ -2322,7 +2480,18 @@ void ClientWebGLContext::BlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum ClientWebGLContext::CheckFramebufferStatus(GLenum target) { if (IsContextLost()) return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; - return Run(target); + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->CheckFramebufferStatus(target); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + GLenum ret = 0; + if (!child->SendCheckFramebufferStatus(target, &ret)) { + ret = 0; + } + return ret; } void ClientWebGLContext::Clear(GLbitfield mask) { @@ -2436,17 +2605,41 @@ void ClientWebGLContext::DepthRange(GLclampf zNear, GLclampf zFar) { void ClientWebGLContext::Flush() { Run(); } -void ClientWebGLContext::Finish() { Run(); } +void ClientWebGLContext::Finish() { + if (IsContextLost()) return; + + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + inProcess->Finish(); + return; + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + (void)child->SendFinish(); +} void ClientWebGLContext::FrontFace(GLenum mode) { Run(mode); } GLenum ClientWebGLContext::GetError() { + const auto notLost = mNotLost; if (mNextError) { const auto ret = mNextError; mNextError = 0; return ret; } - return Run(); + if (IsContextLost()) return 0; + + const auto& inProcess = notLost->inProcess; + if (inProcess) { + return inProcess->GetError(); + } + const auto& child = notLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + GLenum ret = 0; + if (!child->SendGetError(&ret)) { + ret = 0; + } + return ret; } void ClientWebGLContext::Hint(GLenum target, GLenum mode) { @@ -2779,6 +2972,8 @@ void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset, GLuint dstElemCountOverride) { const FuncScope funcScope(*this, "getBufferSubData"); if (IsContextLost()) return; + const auto notLost = + mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. if (!ValidateNonNegative("srcByteOffset", srcByteOffset)) return; uint8_t* bytes; @@ -2787,29 +2982,42 @@ void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset, LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) { return; } - auto view = RawBuffer(byteLen, bytes); + const auto destView = Range{bytes, byteLen}; - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return; const auto& inProcessContext = notLost->inProcess; if (inProcessContext) { - inProcessContext->GetBufferSubData(target, srcByteOffset, view); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetBufferSubData"); + inProcessContext->GetBufferSubData(target, srcByteOffset, destView); + return; } + + const auto& child = notLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + mozilla::ipc::Shmem shmem; + if (!child->SendGetBufferSubData(target, srcByteOffset, destView.length(), + &shmem)) + return; + if (!shmem.IsReadable()) return; + + const auto srcView = ByteRange(shmem); + MOZ_RELEASE_ASSERT(srcView.length() == destView.length()); + Memcpy(destView.begin(), srcView.begin(), srcView.length()); } //// -void ClientWebGLContext::BufferData(GLenum target, WebGLsizeiptr size, +void ClientWebGLContext::BufferData(GLenum target, WebGLsizeiptr rawSize, GLenum usage) { const FuncScope funcScope(*this, "bufferData"); - if (!ValidateNonNegative("size", size)) return; + if (!ValidateNonNegative("size", rawSize)) return; - const auto view = - RawBuffer(static_cast(size), nullptr); - Run(target, view, usage); + const auto size = MaybeAs(rawSize); + if (!size) { + EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "`size` too large for platform."); + return; + } + + const auto range = Range{nullptr, *size}; + Run(target, RawBuffer<>(range), usage); } void ClientWebGLContext::BufferData( @@ -2820,9 +3028,8 @@ void ClientWebGLContext::BufferData( const auto& src = maybeSrc.Value(); src.ComputeState(); - const auto view = RawBuffer(src.Length(), src.Data()); - - Run(target, view, usage); + const auto range = Range{src.Data(), src.Length()}; + Run(target, RawBuffer<>(range), usage); } void ClientWebGLContext::BufferData(GLenum target, @@ -2836,9 +3043,8 @@ void ClientWebGLContext::BufferData(GLenum target, LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) { return; } - - Run(target, RawBuffer(byteLen, bytes), - usage); + const auto range = Range{bytes, byteLen}; + Run(target, RawBuffer<>(range), usage); } //// @@ -2848,8 +3054,8 @@ void ClientWebGLContext::BufferSubData(GLenum target, const dom::ArrayBuffer& src) { const FuncScope funcScope(*this, "bufferSubData"); src.ComputeState(); - Run(target, dstByteOffset, - RawBuffer(src.Length(), src.Data())); + const auto range = Range{src.Data(), src.Length()}; + Run(target, dstByteOffset, RawBuffer<>(range)); } void ClientWebGLContext::BufferSubData(GLenum target, @@ -2864,9 +3070,8 @@ void ClientWebGLContext::BufferSubData(GLenum target, LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) { return; } - - Run(target, dstByteOffset, - RawBuffer(byteLen, bytes)); + const auto range = Range{bytes, byteLen}; + Run(target, dstByteOffset, RawBuffer<>(range)); } void ClientWebGLContext::CopyBufferSubData(GLenum readTarget, @@ -3345,7 +3550,20 @@ void ClientWebGLContext::GetTexParameter( return; } - const auto maybe = Run(tex->mId, pname); + const auto maybe = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetTexParameter(tex->mId, pname); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetTexParameter(tex->mId, pname, &ret)) { + ret.reset(); + } + return ret; + }(); + if (maybe) { switch (pname) { case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: @@ -3583,17 +3801,16 @@ void ClientWebGLContext::CompressedTexImage(bool sub, uint8_t funcDims, return; } - RawBuffer bufferView; + RawBuffer<> range; Maybe pboOffset; if (src.mView) { - const auto range = GetRangeFromView(*src.mView, src.mViewElemOffset, + const auto maybe = GetRangeFromView(*src.mView, src.mViewElemOffset, src.mViewElemLengthOverride); - if (!range) { + if (!maybe) { EnqueueError(LOCAL_GL_INVALID_VALUE, "`source` too small."); return; } - bufferView = - RawBuffer(range->length(), range->begin().get()); + range = RawBuffer<>{*maybe}; } else if (src.mPboOffset) { if (!ValidateNonNegative("offset", *src.mPboOffset)) return; pboOffset = Some(*src.mPboOffset); @@ -3603,8 +3820,7 @@ void ClientWebGLContext::CompressedTexImage(bool sub, uint8_t funcDims, Run( sub, imageTarget, static_cast(level), format, CastUvec3(offset), - CastUvec3(size), bufferView, static_cast(pboImageSize), - pboOffset); + CastUvec3(size), range, static_cast(pboImageSize), pboOffset); } void ClientWebGLContext::CopyTexImage(uint8_t funcDims, GLenum imageTarget, @@ -3664,11 +3880,39 @@ void ClientWebGLContext::ValidateProgram(WebGLProgramJS& prog) const { const FuncScope funcScope(*this, "validateProgram"); if (IsContextLost()) return; if (!prog.ValidateUsable(*this, "prog")) return; - prog.mLastValidate = Run(prog.mId); + + prog.mLastValidate = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->ValidateProgram(prog.mId); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + bool ret = {}; + if (!child->SendValidateProgram(prog.mId, &ret)) { + ret = {}; + } + return ret; + }(); } // ------------------------ Uniforms and attributes ------------------------ +Maybe ClientWebGLContext::GetVertexAttribPriv(const GLuint index, + const GLenum pname) { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetVertexAttrib(index, pname); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetVertexAttrib(index, pname, &ret)) { + ret.reset(); + } + return ret; +} + void ClientWebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname, JS::MutableHandle retval, @@ -3731,7 +3975,7 @@ void ClientWebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, break; } - const auto maybe = Run(index, pname); + const auto maybe = GetVertexAttribPriv(index, pname); if (maybe) { switch (pname) { case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED: @@ -3834,8 +4078,8 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType, // - const auto ptr = bytes.begin().get() + (elemOffset * sizeof(float)); - const auto buffer = RawBuffer(availCount * sizeof(float), ptr); - Run(loc->mLocation, transpose, buffer); + const auto range = Range{ptr, availCount * sizeof(float)}; + Run(loc->mLocation, transpose, RawBuffer<>(range)); } // - @@ -3874,7 +4118,7 @@ WebGLsizeiptr ClientWebGLContext::GetVertexAttribOffset(GLuint index, return 0; } - const auto maybe = Run(index, pname); + const auto maybe = GetVertexAttribPriv(index, pname); if (!maybe) return 0; return *maybe; } @@ -4053,16 +4297,46 @@ void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, void ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc& desc, const Range dest) const { - auto view = RawBufferView(dest); - const auto notLost = mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. if (!notLost) return; - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - inProcessContext->ReadPixels(desc, view); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote ReadPixels"); + const auto& inProcess = notLost->inProcess; + if (inProcess) { + inProcess->ReadPixelsInto(desc, dest); + return; + } + const auto& child = notLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + webgl::ReadPixelsResultIpc res = {}; + if (!child->SendReadPixels(desc, dest.length(), &res)) { + res = {}; + } + if (!res.byteStride) return; + const auto& byteStride = res.byteStride; + const auto& subrect = res.subrect; + const auto& shmemBytes = ByteRange(res.shmem); + + uint8_t bpp; + if (!GetBytesPerPixel(desc.pi, &bpp)) { + MOZ_ASSERT(false); + return; + } + + const auto xByteSize = bpp * static_cast(subrect.width); + const ptrdiff_t byteOffset = subrect.y * byteStride + subrect.x * bpp; + + auto srcItr = shmemBytes.begin() + byteOffset; + auto destItr = dest.begin() + byteOffset; + + for (const auto i : IntegerRange(subrect.height)) { + Unused << i; + + MOZ_RELEASE_ASSERT(srcItr + xByteSize <= shmemBytes.end()); + MOZ_RELEASE_ASSERT(destItr + xByteSize <= dest.end()); + Memcpy(destItr, srcItr, xByteSize); + + srcItr += byteStride; + destItr += byteStride; } } @@ -4144,7 +4418,20 @@ void ClientWebGLContext::GetQueryParameter( if (IsContextLost()) return; if (!query.ValidateUsable(*this, "query")) return; - const auto maybe = Run(query.mId, pname); + const auto maybe = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetQueryParameter(query.mId, pname); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetQueryParameter(query.mId, pname, &ret)) { + ret.reset(); + } + return ret; + }(); + if (maybe) { switch (pname) { case LOCAL_GL_QUERY_RESULT_AVAILABLE: @@ -4247,7 +4534,19 @@ void ClientWebGLContext::GetSamplerParameter( if (IsContextLost()) return; if (!sampler.ValidateUsable(*this, "sampler")) return; - const auto maybe = Run(sampler.mId, pname); + const auto maybe = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetSamplerParameter(sampler.mId, pname); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + Maybe ret; + if (!child->SendGetSamplerParameter(sampler.mId, pname, &ret)) { + ret.reset(); + } + return ret; + }(); if (maybe) { retval.set(JS::NumberValue(*maybe)); } @@ -4341,7 +4640,19 @@ GLenum ClientWebGLContext::ClientWaitSync(WebGLSyncJS& sync, return LOCAL_GL_WAIT_FAILED; } - const auto ret = Run(sync.mId, flags, timeout); + const auto ret = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->ClientWaitSync(sync.mId, flags, timeout); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + GLenum ret = {}; + if (!child->SendClientWaitSync(sync.mId, flags, timeout, &ret)) { + ret = {}; + } + return ret; + }(); switch (ret) { case LOCAL_GL_CONDITION_SATISFIED: @@ -4553,7 +4864,7 @@ void ClientWebGLContext::DrawBuffers(const dom::Sequence& buffers) { void ClientWebGLContext::EnqueueErrorImpl(const GLenum error, const nsACString& text) const { if (!mNotLost) return; // Ignored if context is lost. - Run(error, text.BeginReading()); + Run(error, ToString(text)); } void ClientWebGLContext::RequestExtension(const WebGLExtensionID ext) const { @@ -5005,7 +5316,25 @@ GLint ClientWebGLContext::GetFragDataLocation(const WebGLProgramJS& prog, const auto nameU8 = ToString(NS_ConvertUTF16toUTF8(name)); - return Run(prog.mId, nameU8); + const auto err = CheckGLSLVariableName(mIsWebGL2, nameU8); + if (err) { + EnqueueError(*err); + return -1; + } + + return [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetFragDataLocation(prog.mId, nameU8); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + GLint ret = {}; + if (!child->SendGetFragDataLocation(prog.mId, nameU8, &ret)) { + ret = {}; + } + return ret; + }(); } GLuint ClientWebGLContext::GetUniformBlockIndex( @@ -5179,7 +5508,7 @@ void ClientWebGLContext::GetProgramInfoLog(const WebGLProgramJS& prog, if (!prog.ValidateUsable(*this, "program")) return; const auto& res = GetLinkResult(prog); - retval = NS_ConvertUTF8toUTF16(res.log.c_str()); + retval = NS_ConvertUTF8toUTF16(res.log); } void ClientWebGLContext::GetProgramParameter( @@ -5263,7 +5592,7 @@ void ClientWebGLContext::GetShaderInfoLog(const WebGLShaderJS& shader, if (!shader.ValidateUsable(*this, "shader")) return; const auto& result = GetCompileResult(shader); - retval = NS_ConvertUTF8toUTF16(result.log.c_str()); + retval = NS_ConvertUTF8toUTF16(result.log); } void ClientWebGLContext::GetShaderParameter( @@ -5312,7 +5641,7 @@ void ClientWebGLContext::GetTranslatedShaderSource(const WebGLShaderJS& shader, if (!shader.ValidateUsable(*this, "shader")) return; const auto& result = GetCompileResult(shader); - retval = NS_ConvertUTF8toUTF16(result.translatedSource.c_str()); + retval = NS_ConvertUTF8toUTF16(result.translatedSource); } void ClientWebGLContext::ShaderSource(WebGLShaderJS& shader, @@ -5340,7 +5669,19 @@ void ClientWebGLContext::ShaderSource(WebGLShaderJS& shader, const webgl::CompileResult& ClientWebGLContext::GetCompileResult( const WebGLShaderJS& shader) const { if (shader.mResult.pending) { - shader.mResult = Run(shader.mId); + shader.mResult = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetCompileResult(shader.mId); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + webgl::CompileResult ret = {}; + if (!child->SendGetCompileResult(shader.mId, &ret)) { + ret = {}; + } + return ret; + }(); } return shader.mResult; } @@ -5351,12 +5692,21 @@ const webgl::LinkResult& ClientWebGLContext::GetLinkResult( const auto notLost = mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. if (!notLost) return *(prog.mResult); - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - *(prog.mResult) = inProcessContext->GetLinkResult(prog.mId); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetLinkResult"); - } + + *(prog.mResult) = [&]() { + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetLinkResult(prog.mId); + } + const auto& child = mNotLost->outOfProcess->mWebGLChild; + child->FlushPendingCmds(); + webgl::LinkResult ret; + if (!child->SendGetLinkResult(prog.mId, &ret)) { + ret = {}; + } + return ret; + }(); + prog.mUniformBlockBindings.resize( prog.mResult->active.activeUniformBlocks.size()); diff --git a/dom/canvas/ClientWebGLContext.h b/dom/canvas/ClientWebGLContext.h index a1440d27a8bb..5d69e36f4eaf 100644 --- a/dom/canvas/ClientWebGLContext.h +++ b/dom/canvas/ClientWebGLContext.h @@ -731,13 +731,12 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, const RefPtr mExtLoseContext; - webgl::LossStatus mLossStatus = webgl::LossStatus::Ready; - bool mAwaitingRestore = false; + mutable std::shared_ptr mNotLost; + mutable GLenum mNextError = 0; + mutable webgl::LossStatus mLossStatus = webgl::LossStatus::Ready; + mutable bool mAwaitingRestore = false; // - - private: - std::shared_ptr mNotLost; - mutable GLenum mNextError = 0; public: const auto& Limits() const { return mNotLost->info.limits; } @@ -755,14 +754,14 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, // - public: - void EmulateLoseContext(); - void OnContextLoss(webgl::ContextLossReason); - void RestoreContext(webgl::LossStatus requiredStatus); + void EmulateLoseContext() const; + void OnContextLoss(webgl::ContextLossReason) const; + void RestoreContext(webgl::LossStatus requiredStatus) const; private: bool DispatchEvent(const nsAString&) const; - void Event_webglcontextlost(); - void Event_webglcontextrestored(); + void Event_webglcontextlost() const; + void Event_webglcontextrestored() const; bool CreateHostContext(const uvec2& requestedSize); void ThrowEvent_WebGLContextCreationError(const std::string&) const; @@ -853,6 +852,10 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, EnqueueErrorImpl(error, text); } + void EnqueueError(const webgl::ErrorInfo& info) const { + EnqueueError(info.type, "%s", info.info.c_str()); + } + template void EnqueueWarning(const char* const format, const Args&... args) const { EnqueueError(0, format, args...); @@ -924,9 +927,6 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, nsDisplayListBuilder* aBuilder, layers::WebRenderCanvasData* aCanvasData) override; - bool UpdateCompositableHandle(LayerTransactionChild* aLayerTransaction, - CompositableHandle aHandle) override; - // ------ int32_t GetWidth() override { return AutoAssertCast(DrawingBufferSize().x); } @@ -1026,6 +1026,11 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, void Enable(GLenum cap) const; bool IsEnabled(GLenum cap) const; + private: + Maybe GetNumber(GLenum pname); + Maybe GetString(GLenum pname); + + public: void GetParameter(JSContext* cx, GLenum pname, JS::MutableHandle retval, ErrorResult& rv, bool debug = false); @@ -1703,6 +1708,10 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, } // ------------------------ Uniforms and attributes ------------------------ + + private: + Maybe GetVertexAttribPriv(GLuint index, GLenum pname); + public: void GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname, JS::MutableHandle retval, ErrorResult& rv); @@ -2079,61 +2088,6 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, // The cross-process communication mechanism // ------------------------------------------------------------------------- protected: - /* - template - void DispatchAsync(Args&&... aArgs) const { - const auto& oop = *mNotLost->outOfProcess; - QueueStatus status = oop.mCommandSource->RunAsyncCommand(command, - aArgs...); - if (!IsSuccess(status)) { if (status == QueueStatus::kOOMError) { - JsWarning("Ran out-of-memory during WebGL IPC."); - } - // Not much to do but shut down. Since this was a Pcq failure and - // may have been catastrophic, we don't try to revive it. Make sure to - // post "webglcontextlost" - MOZ_ASSERT_UNREACHABLE( - "TODO: Make this shut down the context, actors, everything."); - } - } - - template - ReturnType DispatchSync(Args&&... aArgs) const { - const auto& oop = *mNotLost->outOfProcess; - ReturnType returnValue; - QueueStatus status = - oop.mCommandSource->RunSyncCommand(command, returnValue, aArgs...); - - if (!IsSuccess(status)) { - if (status == QueueStatus::kOOMError) { - JsWarning("Ran out-of-memory during WebGL IPC."); - } - // Not much to do but shut down. Since this was a Pcq failure and - // may have been catastrophic, we don't try to revive it. Make sure to - // post "webglcontextlost" - MOZ_ASSERT_UNREACHABLE( - "TODO: Make this shut down the context, actors, everything."); - } - return returnValue; - } - - template - void DispatchVoidSync(Args&&... aArgs) const { - const auto& oop = *mNotLost->outOfProcess; - const auto status = - oop.mCommandSource->RunVoidSyncCommand(command, aArgs...); - if (!IsSuccess(status)) { - if (status == QueueStatus::kOOMError) { - JsWarning("Ran out-of-memory during WebGL IPC."); - } - // Not much to do but shut down. Since this was a Pcq failure and - // may have been catastrophic, we don't try to revive it. Make sure to - // post "webglcontextlost" - MOZ_ASSERT_UNREACHABLE( - "TODO: Make this shut down the context, actors, everything."); - } - } - */ - template friend struct WebGLClientDispatcher; @@ -2143,16 +2097,8 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, // If we are running WebGL in this process then call the HostWebGLContext // method directly. Otherwise, dispatch over IPC. - // template ::ReturnType, size_t Id = - // WebGLMethodDispatcher::Id(), typename... Args> - // ReturnType Run(Args&&... aArgs) const; - template < - typename MethodType, MethodType method, - typename ReturnType = typename FunctionTypeTraits::ReturnType, - typename... Args> - ReturnType Run(Args&&... aArgs) const; + template + void Run(Args&&... aArgs) const; // ------------------------------------------------------------------------- // Helpers for DOM operations, composition, actors, etc diff --git a/dom/canvas/HostWebGLContext.h b/dom/canvas/HostWebGLContext.h index b50ec187b5f3..eb240256bab6 100644 --- a/dom/canvas/HostWebGLContext.h +++ b/dom/canvas/HostWebGLContext.h @@ -191,11 +191,16 @@ class HostWebGLContext final : public SupportsWeakPtr { Maybe GetFrontBuffer(ObjectId xrFb, const bool webvr) const; - RefPtr GetFrontBufferSnapshot() const { - return mContext->GetFrontBufferSnapshot(); + // - + + uvec2 GetFrontBufferSize() const { return mContext->DrawingBufferSize(); } + bool FrontBufferSnapshotInto(Range dest) const { + return mContext->FrontBufferSnapshotInto(dest); } - void ClearVRSwapChain() { mContext->ClearVRSwapChain(); } + void ClearVRSwapChain() const { mContext->ClearVRSwapChain(); } + + // - void Resize(const uvec2& size) { return mContext->Resize(size); } @@ -258,7 +263,7 @@ class HostWebGLContext final : public SupportsWeakPtr { bool IsEnabled(GLenum cap) const { return mContext->IsEnabled(cap); } - Maybe GetParameter(GLenum pname) const { + Maybe GetNumber(GLenum pname) const { return mContext->GetParameter(pname); } @@ -477,21 +482,21 @@ class HostWebGLContext final : public SupportsWeakPtr { writeOffset, size); } - void GetBufferSubData(GLenum target, uint64_t srcByteOffset, - RawBuffer& dest) const { - const auto range = MakeRange(dest); - GetWebGL2Context()->GetBufferSubData(target, srcByteOffset, range); + bool GetBufferSubData(GLenum target, uint64_t srcByteOffset, + const Range& dest) const { + return GetWebGL2Context()->GetBufferSubData(target, srcByteOffset, dest); } - void BufferData(GLenum target, const RawBuffer& data, - GLenum usage) const { - mContext->BufferData(target, data.Length(), data.Data(), usage); + void BufferData(GLenum target, const RawBuffer<>& data, GLenum usage) const { + const auto& range = data.Data(); + mContext->BufferData(target, range.length(), range.begin().get(), usage); } void BufferSubData(GLenum target, uint64_t dstByteOffset, - const RawBuffer& srcData) const { - mContext->BufferSubData(target, dstByteOffset, srcData.Length(), - srcData.Data()); + const RawBuffer<>& srcData) const { + const auto& range = srcData.Data(); + mContext->BufferSubData(target, dstByteOffset, range.length(), + range.begin().get()); } // -------------------------- Framebuffer Objects -------------------------- @@ -550,8 +555,7 @@ class HostWebGLContext final : public SupportsWeakPtr { // CompressedTexSubImage if `sub` void CompressedTexImage(bool sub, GLenum imageTarget, uint32_t level, GLenum format, const uvec3& offset, const uvec3& size, - const RawBuffer& src, - const uint32_t pboImageSize, + const RawBuffer<>& src, const uint32_t pboImageSize, const Maybe& pboOffset) const { mContext->CompressedTexImage(sub, imageTarget, level, format, offset, size, MakeRange(src), pboImageSize, pboOffset); @@ -602,8 +606,8 @@ class HostWebGLContext final : public SupportsWeakPtr { // ------------------------ Uniforms and attributes ------------------------ void UniformData(uint32_t loc, bool transpose, - const RawBuffer& data) const { - mContext->UniformData(loc, transpose, MakeRange(data)); + const RawBuffer<>& data) const { + mContext->UniformData(loc, transpose, data.Data()); } void VertexAttrib4T(GLuint index, const webgl::TypedQuad& data) const { @@ -660,10 +664,9 @@ class HostWebGLContext final : public SupportsWeakPtr { mContext->ReadPixelsPbo(desc, offset); } - void ReadPixels(const webgl::ReadPixelsDesc& desc, - RawBuffer& dest) const { - const auto range = MakeRange(dest); - mContext->ReadPixels(desc, range); + webgl::ReadPixelsResult ReadPixelsInto(const webgl::ReadPixelsDesc& desc, + const Range& dest) const { + return mContext->ReadPixelsInto(desc, dest); } // ----------------------------- Sampler ----------------------------------- diff --git a/dom/canvas/IpdlQueue.h b/dom/canvas/IpdlQueue.h index b3be6b386a27..93cc2eddec87 100644 --- a/dom/canvas/IpdlQueue.h +++ b/dom/canvas/IpdlQueue.h @@ -83,10 +83,6 @@ struct IpdlQueueBuffer { using IpdlQueueBuffers = nsTArray; -// Any object larger than this will be inserted into its own Shmem. -// TODO: Base this on something. -static constexpr size_t kMaxIpdlQueueArgSize = 256 * 1024; - static constexpr uint32_t kAsyncFlushWaitMs = 4; // 4ms template @@ -432,10 +428,6 @@ class IpdlProducer final : public SupportsWeakPtr> { arg, aArgSize); } - inline bool NeedsSharedMemory(size_t aRequested) { - return aRequested >= kMaxIpdlQueueArgSize; - } - base::ProcessId OtherPid() { return mActor ? mActor->OtherPid() : 0; } protected: @@ -543,10 +535,6 @@ class IpdlConsumer final : public SupportsWeakPtr> { mBuf.Elements(), mBuf.Length() + 1, aRead, aWrite, arg, aArgSize); } - static inline bool NeedsSharedMemory(size_t aRequested) { - return aRequested >= kMaxIpdlQueueArgSize; - } - base::ProcessId OtherPid() { return mActor ? mActor->OtherPid() : 0; } protected: diff --git a/dom/canvas/PWebGL.ipdl b/dom/canvas/PWebGL.ipdl index 8319aa8f2540..74c8b271dc46 100644 --- a/dom/canvas/PWebGL.ipdl +++ b/dom/canvas/PWebGL.ipdl @@ -9,10 +9,25 @@ include protocol PCompositorBridge; include protocol PLayerTransaction; using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h"; -using mozilla::webgl::ContextLossReason from "mozilla/dom/WebGLIpdl.h"; +using mozilla::layers::SurfaceDescriptor from "mozilla/layers/LayersTypes.h"; using std::string from "ipc/IPCMessageUtils.h"; +using mozilla::uvec2 from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::CompileResult from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::ContextLossReason from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::FrontBufferSnapshotIpc from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::GetUniformData from "mozilla/dom/WebGLIpdl.h"; using mozilla::webgl::InitContextDesc from "mozilla/dom/WebGLIpdl.h"; using mozilla::webgl::InitContextResult from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::Int32Vector from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::LinkResult from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::MaybeDouble from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::MaybeFrontBufferSnapshotIpc from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::MaybeShaderPrecisionFormat from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::MaybeSurfaceDescriptor from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::MaybeString from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::OpaqueFramebufferOptions from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::ReadPixelsDesc from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::ReadPixelsResultIpc from "mozilla/dom/WebGLIpdl.h"; using mozilla::HostWebGLCommandSinkP from "mozilla/dom/WebGLCrossProcessCommandQueue.h"; using mozilla::HostWebGLCommandSinkI from "mozilla/dom/WebGLCrossProcessCommandQueue.h"; using mozilla::dom::IpdlQueueBuffer from "mozilla/dom/IpdlQueue.h"; @@ -37,20 +52,53 @@ parent: async __delete__(); - // DLP: TODO: Does this need to be sync? - sync UpdateCompositableHandle(PLayerTransaction aLayerTrans, - CompositableHandle aHandle); + // - - sync ExchangeIpdlQueueData(IpdlQueueBuffer aMsg) returns (IpdlQueueBuffers aResponse); + async DispatchCommands(Shmem commands, uint64_t size); + + // - + + sync GetFrontBufferSnapshot() returns (FrontBufferSnapshotIpc ret); + sync ReadPixels(ReadPixelsDesc desc, uint64_t maxBytes) returns (ReadPixelsResultIpc ret); + + // - + + sync CheckFramebufferStatus(uint32_t target) returns (uint32_t ret); + sync ClientWaitSync(uint64_t id, uint32_t flags, uint64_t timeout) returns (uint32_t ret); + sync CreateOpaqueFramebuffer(uint64_t id, OpaqueFramebufferOptions options) returns (bool ret); + sync DrawingBufferSize() returns (uvec2 ret); + sync Finish(); + sync GetBufferParameter(uint32_t target, uint32_t pname) returns (MaybeDouble ret); + sync GetBufferSubData(uint32_t target, uint64_t srcByteOffset, uint64_t byteSize) returns (Shmem ret); + sync GetCompileResult(uint64_t id) returns (CompileResult ret); + sync GetError() returns (uint32_t ret); + sync GetFragDataLocation(uint64_t id, string name) returns (int32_t ret); + sync GetFramebufferAttachmentParameter(uint64_t id, + uint32_t attachment, + uint32_t pname) returns (MaybeDouble ret); + sync GetFrontBuffer(uint64_t fb, bool vr) returns (MaybeSurfaceDescriptor ret); + sync GetIndexedParameter(uint32_t target, uint32_t index) returns (MaybeDouble ret); + sync GetInternalformatParameter(uint32_t target, uint32_t internalFormat, uint32_t pname) returns (Int32Vector? ret); + sync GetLinkResult(uint64_t id) returns (LinkResult ret); + sync GetNumber(uint32_t pname) returns (MaybeDouble ret); + sync GetQueryParameter(uint64_t id, uint32_t pname) returns (MaybeDouble ret); + sync GetRenderbufferParameter(uint64_t id, uint32_t pname) returns (MaybeDouble ret); + sync GetSamplerParameter(uint64_t id, uint32_t pname) returns (MaybeDouble ret); + sync GetShaderPrecisionFormat( + uint32_t shaderType, uint32_t precisionType) returns (MaybeShaderPrecisionFormat ret); + sync GetString(uint32_t pname) returns (MaybeString ret); + sync GetTexParameter(uint64_t id, uint32_t pname) returns (MaybeDouble ret); + sync GetUniform(uint64_t id, uint32_t loc) returns (GetUniformData ret); + sync GetVertexAttrib(uint32_t index, uint32_t pname) returns (MaybeDouble ret); + sync IsEnabled(uint32_t cap) returns (bool ret); + sync OnMemoryPressure(); + sync ValidateProgram(uint64_t id) returns (bool ret); child: async JsWarning(string text); // Tell client that this queue needs to be shut down async OnContextLoss(ContextLossReason aReason); - -both: - async TransmitIpdlQueueData(IpdlQueueBuffer aData); }; } // dom diff --git a/dom/canvas/QueueParamTraits.h b/dom/canvas/QueueParamTraits.h index 038624e2a6d4..59b16877be29 100644 --- a/dom/canvas/QueueParamTraits.h +++ b/dom/canvas/QueueParamTraits.h @@ -21,36 +21,30 @@ namespace mozilla { namespace webgl { -struct QueueStatus { - enum EStatus { - // Operation was successful - kSuccess, - // The operation failed because the queue isn't ready for it. - // Either the queue is too full for an insert or too empty for a remove. - // The operation may succeed if retried. - kNotReady, - // The operation required more room than the queue supports. - // It should not be retried -- it will always fail. - kTooSmall, - // The operation failed for some reason that is unrecoverable. - // All values below this value indicate a fata error. - kFatalError, - // Fatal error: Internal processing ran out of memory. This is likely e.g. - // during de-serialization. - kOOMError, - } mValue; - - MOZ_IMPLICIT QueueStatus(const EStatus status = kSuccess) : mValue(status) {} - explicit operator bool() const { return mValue == kSuccess; } - explicit operator int() const { return static_cast(mValue); } - bool operator==(const EStatus& o) const { return mValue == o; } - bool operator!=(const EStatus& o) const { return !(*this == o); } +enum class QueueStatus { + // Operation was successful + kSuccess, + // The operation failed because the queue isn't ready for it. + // Either the queue is too full for an insert or too empty for a remove. + // The operation may succeed if retried. + kNotReady, + // The operation required more room than the queue supports. + // It should not be retried -- it will always fail. + kTooSmall, + // The operation failed for some reason that is unrecoverable. + // All values below this value indicate a fata error. + kFatalError, + // Fatal error: Internal processing ran out of memory. This is likely e.g. + // during de-serialization. + kOOMError, }; inline bool IsSuccess(QueueStatus status) { return status == QueueStatus::kSuccess; } +inline bool operator!(const QueueStatus status) { return !IsSuccess(status); } + template struct RemoveCVR { typedef typename std::remove_reference::type>::type @@ -84,35 +78,19 @@ struct IsTriviallySerializable * goes for TryRemove, which fails when there isn't enough data in * the queue yet for them to complete. * - * QueueParamTraits resolve this problem by allowing the Try... operations to - * use QueueParamTraits::Type>::MinSize() to get a - * lower-bound on the amount of room in the queue required for Arg. If the - * operation needs more than is available then the operation quickly fails. - * Otherwise, (de)serialization will commence, although it may still fail if - * MinSize() was too low. - * * Their expected interface is: * * template<> struct QueueParamTraits::Type> { - * // Write data from aArg into the PCQ. It is an error to write less than - * // is reported by MinSize(aArg). + * // Write data from aArg into the PCQ. * static QueueStatus Write(ProducerView& aProducerView, const Arg& aArg) * {...}; * * // Read data from the PCQ into aArg, or just skip the data if aArg is null. - * // It is an error to read less than is reported by MinSize(aArg). * static QueueStatus Read(ConsumerView& aConsumerView, Arg* aArg) {...} - * - * // The minimum number of bytes needed to represent this object in the - * // queue. It is intended to be a very fast estimate but most cases can - * // easily compute the exact value. - * // It is an error for the queue to require less room than MinSize() - * // reports. A MinSize of 0 is always valid (albeit wasteful). - * static size_t MinSize(const Arg& aArg) {...} * }; */ template -struct QueueParamTraits; +struct QueueParamTraits; // Todo: s/QueueParamTraits/SizedParamTraits/ /** * The marshaller handles all data insertion into the queue. @@ -182,15 +160,12 @@ class ProducerView { * Copy bytes from aBuffer to the producer if there is enough room. * aBufferSize must not be 0. */ - inline QueueStatus Write(const void* aBuffer, size_t aBufferSize); + inline QueueStatus Write(const uint8_t* begin, const uint8_t* end); - /** - * Copy bytes from src (an array of Ts), to the producer if there is - * enough room. count is the number of elements in the array. - */ template - inline QueueStatus Write(const T* src, size_t count) { - return Write(reinterpret_cast(src), count * sizeof(T)); + inline QueueStatus Write(const T* begin, const T* end) { + return Write(reinterpret_cast(begin), + reinterpret_cast(end)); } /** @@ -202,17 +177,6 @@ class ProducerView { typename RemoveCVR::Type>::Write(*this, aArg); } - /** - * MinSize of Arg using QueueParamTraits. - */ - template - size_t MinSizeParam(const Arg& aArg = nullptr) { - return mozilla::webgl::QueueParamTraits< - typename RemoveCVR::Type>::MinSize(*this, aArg); - } - - inline size_t MinSizeBytes(size_t aNBytes); - QueueStatus GetStatus() { return mStatus; } private: @@ -241,16 +205,12 @@ class ConsumerView { * Read bytes from the consumer if there is enough data. aBuffer may * be null (in which case the data is skipped) */ - inline QueueStatus Read(void* aBuffer, size_t aBufferSize); + inline QueueStatus Read(uint8_t* begin, uint8_t* end); - /** - * Read bytes from the consumer into an array of Ts, if there is enough data. - * aBuffer may be null (in which case the data is skipped). count is the - * number of array elements to read. - */ template - inline QueueStatus Read(T* dest, size_t count) { - return Read(reinterpret_cast(dest), count * sizeof(T)); + inline QueueStatus Read(T* begin, T* end) { + return Read(reinterpret_cast(begin), + reinterpret_cast(end)); } /** @@ -260,22 +220,10 @@ class ConsumerView { template QueueStatus ReadParam(Arg* aArg) { MOZ_ASSERT(aArg); - return mozilla::webgl::QueueParamTraits*>::Read(*this, - aArg); + return mozilla::webgl::QueueParamTraits>::Read(*this, + aArg); } - /** - * MinSize of Arg using QueueParamTraits. aArg may be null. - */ - template - size_t MinSizeParam(Arg& aArg) { - MOZ_ASSERT(aArg); - return mozilla::webgl::QueueParamTraits&>::MinSize( - *this, aArg); - } - - inline size_t MinSizeBytes(size_t aNBytes); - QueueStatus GetStatus() { return mStatus; } private: @@ -286,60 +234,22 @@ class ConsumerView { }; template -QueueStatus ProducerView::Write(const void* aBuffer, size_t aBufferSize) { - MOZ_ASSERT(aBuffer && (aBufferSize > 0)); - if (!mStatus) { - return mStatus; +QueueStatus ProducerView::Write(const uint8_t* begin, const uint8_t* end) { + MOZ_ASSERT(begin); + MOZ_ASSERT(begin < end); + if (IsSuccess(mStatus)) { + mStatus = mProducer->WriteObject(mRead, mWrite, begin, end - begin); } - - if (mProducer->NeedsSharedMemory(aBufferSize)) { - mozilla::ipc::Shmem shmem; - QueueStatus status = mProducer->AllocShmem(&shmem, aBufferSize, aBuffer); - if (!IsSuccess(status)) { - return status; - } - return WriteParam(std::move(shmem)); - } - - return mProducer->WriteObject(mRead, mWrite, aBuffer, aBufferSize); + return mStatus; } template -size_t ProducerView::MinSizeBytes(size_t aNBytes) { - mozilla::ipc::Shmem shmem; - return mProducer->NeedsSharedMemory(aNBytes) ? MinSizeParam(shmem) : aNBytes; -} - -template -QueueStatus ConsumerView::Read(void* aBuffer, size_t aBufferSize) { - MOZ_ASSERT(aBufferSize > 0); - if (!mStatus) { - return mStatus; +QueueStatus ConsumerView::Read(uint8_t* begin, uint8_t* end) { + MOZ_ASSERT(begin < end); + if (IsSuccess(mStatus)) { + mStatus = mConsumer->ReadObject(mRead, mWrite, begin, end - begin); } - - if (mConsumer->NeedsSharedMemory(aBufferSize)) { - mozilla::ipc::Shmem shmem; - QueueStatus status = ReadParam(&shmem); - if (!IsSuccess(status)) { - return status; - } - - if ((shmem.Size() != aBufferSize) || (!shmem.get())) { - return QueueStatus::kFatalError; - } - - if (aBuffer) { - memcpy(aBuffer, shmem.get(), aBufferSize); - } - return QueueStatus::kSuccess; - } - return mConsumer->ReadObject(mRead, mWrite, aBuffer, aBufferSize); -} - -template -size_t ConsumerView::MinSizeBytes(size_t aNBytes) { - mozilla::ipc::Shmem shmem; - return mConsumer->NeedsSharedMemory(aNBytes) ? MinSizeParam(shmem) : aNBytes; + return mStatus; } // --------------------------------------------------------------- @@ -355,7 +265,8 @@ struct QueueParamTraits { "No QueueParamTraits specialization was found for this type " "and it does not satisfy IsTriviallySerializable."); // Write self as binary - return aProducerView.Write(&aArg, sizeof(Arg)); + const auto begin = &aArg; + return aProducerView.Write(begin, begin + 1); } template @@ -364,15 +275,7 @@ struct QueueParamTraits { "No QueueParamTraits specialization was found for this type " "and it does not satisfy IsTriviallySerializable."); // Read self as binary - return aConsumerView.Read(aArg, sizeof(Arg)); - } - - template - static constexpr size_t MinSize(View& aView, const Arg& aArg) { - static_assert(mozilla::webgl::template IsTriviallySerializable::value, - "No QueueParamTraits specialization was found for this type " - "and it does not satisfy IsTriviallySerializable."); - return sizeof(Arg); + return aConsumerView.Read(aArg, aArg + 1); } }; @@ -387,9 +290,9 @@ struct EnumSerializer { typedef typename std::underlying_type::type DataType; template - static void Write(ProducerView& aProducerView, const ParamType& aValue) { + static auto Write(ProducerView& aProducerView, const ParamType& aValue) { MOZ_RELEASE_ASSERT(EnumValidator::IsLegalValue(aValue)); - aProducerView.WriteParam(DataType(aValue)); + return aProducerView.WriteParam(DataType(aValue)); } template @@ -398,21 +301,16 @@ struct EnumSerializer { if (!aConsumerView.ReadParam(&value)) { CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::IPCReadErrorReason, "Bad iter"_ns); - return aConsumerView.GetStatus(); + return IsSuccess(aConsumerView.GetStatus()); } if (!EnumValidator::IsLegalValue(ParamType(value))) { CrashReporter::AnnotateCrashReport( CrashReporter::Annotation::IPCReadErrorReason, "Illegal value"_ns); - return aConsumerView.GetStatus(); + return IsSuccess(aConsumerView.GetStatus()); } *aResult = ParamType(value); - return QueueStatus::kSuccess; - } - - template - static constexpr size_t MinSize(View& aView, const ParamType& aArg) { - return aView.MinSizeParam(DataType(aArg)); + return true; } }; @@ -432,31 +330,9 @@ struct ContiguousEnumSerializerInclusive // --------------------------------------------------------------- template <> -struct QueueParamTraits +struct QueueParamTraits : public ContiguousEnumSerializerInclusive< - QueueStatus::EStatus, QueueStatus::EStatus::kSuccess, - QueueStatus::EStatus::kOOMError> {}; - -template <> -struct QueueParamTraits { - using ParamType = QueueStatus; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - return aProducerView.WriteParam(aArg.mValue); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - return aConsumerView.ReadParam(&aArg->mValue); - } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - return aView.MinSize(aArg.mValue); - } -}; + QueueStatus, QueueStatus::kSuccess, QueueStatus::kOOMError> {}; // --------------------------------------------------------------- @@ -482,7 +358,7 @@ struct QueueParamTraits { template static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { bool isVoid = false; - if (!aConsumerView.ReadParam(&isVoid)) { + if (!IsSuccess(aConsumerView.ReadParam(&isVoid))) { return aConsumerView.GetStatus(); } aArg->SetIsVoid(isVoid); @@ -491,7 +367,7 @@ struct QueueParamTraits { } uint32_t len = 0; - if (!aConsumerView.ReadParam(&len)) { + if (!IsSuccess(aConsumerView.ReadParam(&len))) { return aConsumerView.GetStatus(); } @@ -504,24 +380,13 @@ struct QueueParamTraits { if (!buf) { return QueueStatus::kOOMError; } - if (!aConsumerView.Read(buf, len)) { + if (!IsSuccess(aConsumerView.Read(buf, len))) { return aConsumerView.GetStatus(); } buf[len] = '\0'; aArg->Adopt(buf, len); return QueueStatus::kSuccess; } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - size_t minSize = aView.template MinSizeParam(aArg.IsVoid()); - if (aArg.IsVoid()) { - return minSize; - } - minSize += aView.template MinSizeParam(aArg.Length()) + - aView.MinSizeBytes(aArg.Length()); - return minSize; - } }; template <> @@ -581,18 +446,6 @@ struct QueueParamTraits { aArg->Adopt(buf, len); return QueueStatus::kSuccess; } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - size_t minSize = aView.template MinSizeParam(aArg.IsVoid()); - if (aArg.IsVoid()) { - return minSize; - } - uint32_t sizeofchar = sizeof(typename ParamType::char_type); - minSize += aView.template MinSizeParam(aArg.Length()) + - aView.MinSizeBytes(aArg.Length() * sizeofchar); - return minSize; - } }; template <> @@ -645,15 +498,6 @@ struct NSArrayQueueParamTraits, false> { } return aConsumerView.GetStatus(); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - size_t ret = aView.MinSizeParam(aArg.Length()); - for (auto& elt : aArg) { - ret += aView.MinSizeParam(elt); - } - return ret; - } }; // For ElementTypes that are IsTriviallySerializable @@ -684,13 +528,6 @@ struct NSArrayQueueParamTraits, true> { return aConsumerView.Read(aArg->Elements(), arrayLen * sizeof(ElementType)); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - size_t ret = aView.template MinSizeParam(aArg.Length()); - ret += aView.MinSizeBytes(aArg.Length() * sizeof(ElementType)); - return ret; - } }; template @@ -728,15 +565,6 @@ struct ArrayQueueParamTraits, false> { } return aConsumerView.GetStatus(); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - size_t ret = 0; - for (const auto& elt : aArg) { - ret += aView.MinSizeParam(elt); - } - return ret; - } }; // For ElementTypes that are IsTriviallySerializable @@ -755,11 +583,6 @@ struct ArrayQueueParamTraits, true> { static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { return aConsumerView.Read(aArg->begin(), sizeof(ElementType[Length])); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - return aView.MinSizeBytes(sizeof(ElementType[Length])); - } }; template @@ -797,12 +620,6 @@ struct QueueParamTraits> { aArg->emplace(); return aConsumerView.ReadParam(aArg->ptr()); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - return aView.template MinSizeParam(aArg) + - (aArg.isSome() ? aView.MinSizeParam(aArg.ref()) : 0); - } }; // --------------------------------------------------------------- @@ -836,12 +653,6 @@ struct QueueParamTraits>> { aArg->emplace(VariantType()); return aConsumerView.ReadParam(aArg->ptr()); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - return aView.MinSizeParam(aArg.mIsSome) + - (aArg.isSome() ? aView.MinSizeParam(aArg.ref()) : 0); - } }; // --------------------------------------------------------------- @@ -862,11 +673,6 @@ struct QueueParamTraits> { aConsumerView.ReadParam(aArg->first()); return aConsumerView.ReadParam(aArg->second()); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - return aView.MinSizeParam(aArg.first()) + aView.MinSizeParam(aArg.second()); - } }; // --------------------------------------------------------------- @@ -905,105 +711,10 @@ struct QueueParamTraits> { aArg->reset(obj); return aConsumerView.ReadParam(obj); } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - size_t ret = aView.template MinSizeParam(aArg); - return ret + aView.MinSizeParam(*aArg); - } }; // --------------------------------------------------------------- -// C++ does not allow this struct with a templated method to be local to -// another struct (QueueParamTraits>) so we put it here. -template -struct PcqVariantWriter { - ProducerView& mView; - template - QueueStatus match(const T& x) { - return mView.WriteParam(x); - } -}; - -template -struct QueueParamTraits> { - using ParamType = Variant; - using Tag = typename mozilla::detail::VariantTag::Type; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - aProducerView.WriteParam(aArg.tag); - return aArg.match(PcqVariantWriter{aProducerView}); - } - - // Check the N-1th tag. See ParamTraits for details. - template - struct VariantReader { - using Next = VariantReader; - template - static QueueStatus Read(ConsumerView& aView, Tag aTag, ParamType* aArg) { - if (aTag == N - 1) { - using EntryType = typename mozilla::detail::Nth::Type; - return aView.ReadParam(*static_cast(aArg->ptr())); - } - return Next::Read(aView, aTag, aArg); - } - }; - - template - struct VariantReader<0, dummy> { - template - static QueueStatus Read(ConsumerView& aView, Tag aTag, ParamType* aArg) { - MOZ_ASSERT_UNREACHABLE("Tag wasn't for an entry in this Variant"); - return QueueStatus::kFatalError; - } - }; - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - Tag tag; - if (!aConsumerView.ReadParam(&tag)) { - return aConsumerView.GetStatus(); - } - aArg->tag = tag; - return VariantReader::Read(aConsumerView, tag, aArg); - } - - // Get the min size of the given variant or get the min size of all of the - // variant's types. - template - struct MinSizeVariant { - using Next = MinSizeVariant; - static size_t MinSize(View& aView, const Tag* aTag, const ParamType& aArg) { - using EntryType = typename mozilla::detail::Nth::Type; - MOZ_ASSERT(aTag); - if (*aTag == N - 1) { - return aView.MinSizeParam(aArg.template as()); - } - return Next::MinSize(aView, aTag, aArg); - } - }; - - template - struct MinSizeVariant<0, View> { - // We've reached the end of the type list. We will legitimately get here - // when calculating MinSize for a null Variant. - static size_t MinSize(View& aView, const Tag* aTag, const ParamType& aArg) { - MOZ_ASSERT_UNREACHABLE("Tag wasn't for an entry in this Variant"); - return 0; - } - }; - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - return aView.MinSizeParam(aArg.tag) + - MinSizeVariant::MinSize(aView, aArg.tag, - aArg); - } -}; - template <> struct QueueParamTraits { using ParamType = mozilla::ipc::Shmem; @@ -1036,12 +747,6 @@ struct QueueParamTraits { rawmem, id); return QueueStatus::kSuccess; } - - template - static size_t MinSize(View& aView, const ParamType& aArg) { - return aView.MinSizeParam( - aArg.Id(mozilla::ipc::Shmem::PrivateIPDLCaller())); - } }; } // namespace webgl diff --git a/dom/canvas/WebGL2Context.h b/dom/canvas/WebGL2Context.h index ae0e541b246a..60a8ce429463 100644 --- a/dom/canvas/WebGL2Context.h +++ b/dom/canvas/WebGL2Context.h @@ -36,7 +36,7 @@ class WebGL2Context final : public WebGLContext { void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, uint64_t readOffset, uint64_t writeOffset, uint64_t size) const; - void GetBufferSubData(GLenum target, uint64_t srcByteOffset, + bool GetBufferSubData(GLenum target, uint64_t srcByteOffset, const Range& dest) const; // ------------------------------------------------------------------------- diff --git a/dom/canvas/WebGL2ContextBuffers.cpp b/dom/canvas/WebGL2ContextBuffers.cpp index 7008ddcd2f45..ebbda995ee55 100644 --- a/dom/canvas/WebGL2ContextBuffers.cpp +++ b/dom/canvas/WebGL2ContextBuffers.cpp @@ -78,23 +78,23 @@ void WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, writeBuffer->ResetLastUpdateFenceId(); } -void WebGL2Context::GetBufferSubData(GLenum target, uint64_t srcByteOffset, +bool WebGL2Context::GetBufferSubData(GLenum target, uint64_t srcByteOffset, const Range& dest) const { const FuncScope funcScope(*this, "getBufferSubData"); - if (IsContextLost()) return; + if (IsContextLost()) return false; const auto& buffer = ValidateBufferSelection(target); - if (!buffer) return; + if (!buffer) return false; const auto byteLen = dest.length(); - if (!buffer->ValidateRange(srcByteOffset, byteLen)) return; + if (!buffer->ValidateRange(srcByteOffset, byteLen)) return false; //// if (!CheckedInt(srcByteOffset).isValid() || !CheckedInt(byteLen).isValid()) { ErrorOutOfMemory("offset or size too large for platform."); - return; + return false; } const GLsizeiptr glByteLen(byteLen); @@ -144,6 +144,7 @@ void WebGL2Context::GetBufferSubData(GLenum target, uint64_t srcByteOffset, gl->fBindTransformFeedback(LOCAL_GL_TRANSFORM_FEEDBACK, tfo); } } + return true; } } // namespace mozilla diff --git a/dom/canvas/WebGLChild.cpp b/dom/canvas/WebGLChild.cpp index 6df9182c51a5..8338cae98228 100644 --- a/dom/canvas/WebGLChild.cpp +++ b/dom/canvas/WebGLChild.cpp @@ -16,6 +16,45 @@ WebGLChild::WebGLChild(ClientWebGLContext& context) WebGLChild::~WebGLChild() { (void)Send__delete__(this); } +// - + +Maybe> WebGLChild::AllocPendingCmdBytes(const size_t size) { + if (!mPendingCmds) { + mPendingCmds.reset(new webgl::ShmemCmdBuffer); + size_t capacity = 1000 * 1000; + if (capacity < size) { + capacity = size; + } + + if (!PWebGLChild::AllocShmem( + capacity, mozilla::ipc::SharedMemory::SharedMemoryType::TYPE_BASIC, + &(mPendingCmds->mShmem))) { + return {}; + } + } + + auto remaining = mPendingCmds->Remaining(); + if (size > remaining.length()) { + FlushPendingCmds(); + return AllocPendingCmdBytes(size); + } + mPendingCmds->mPos += size; + return Some(Range{remaining.begin(), remaining.begin() + size}); +} + +void WebGLChild::FlushPendingCmds() { + if (!mPendingCmds) return; + + const auto cmdBytes = mPendingCmds->mPos; + SendDispatchCommands(std::move(mPendingCmds->mShmem), cmdBytes); + mPendingCmds = nullptr; + + mFlushedCmdInfo.flushes += 1; + mFlushedCmdInfo.flushedCmdBytes += cmdBytes; +} + +// - + mozilla::ipc::IPCResult WebGLChild::RecvJsWarning( const std::string& text) const { mContext.JsWarning(text); diff --git a/dom/canvas/WebGLChild.h b/dom/canvas/WebGLChild.h index 2cff42045bac..45b7a328ecc6 100644 --- a/dom/canvas/WebGLChild.h +++ b/dom/canvas/WebGLChild.h @@ -27,11 +27,17 @@ class ClientWebGLContext; namespace dom { +struct FlushedCmdInfo final { + size_t flushes = 0; + size_t flushedCmdBytes = 0; +}; + class WebGLChild final : public PWebGLChild, - public SyncProducerActor, - public AsyncConsumerActor, public SupportsWeakPtr, public mozilla::webgl::PcqActor { + std::unique_ptr mPendingCmds; + FlushedCmdInfo mFlushedCmdInfo; + public: MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLChild) NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebGLChild, override); @@ -44,6 +50,9 @@ class WebGLChild final : public PWebGLChild, // For SyncProducerActor: static IpdlQueueProtocol GetIpdlQueueProtocol(size_t aCmd, ...); + Maybe> AllocPendingCmdBytes(size_t); + void FlushPendingCmds(); + private: friend PWebGLChild; virtual ~WebGLChild(); diff --git a/dom/canvas/WebGLCommandQueue.h b/dom/canvas/WebGLCommandQueue.h index e3f3c52684b9..4d5deb09f3c2 100644 --- a/dom/canvas/WebGLCommandQueue.h +++ b/dom/canvas/WebGLCommandQueue.h @@ -6,10 +6,12 @@ #ifndef WEBGLCOMMANDQUEUE_H_ #define WEBGLCOMMANDQUEUE_H_ +#include #include "mozilla/FunctionTypeTraits.h" #include "mozilla/dom/ProducerConsumerQueue.h" #include "mozilla/ipc/IPDLParamTraits.h" -#include +#include "QueueParamTraits.h" +#include "WebGLTypes.h" // Get around a bug in Clang related to __thiscall method pointers #if defined(_M_IX86) @@ -20,8 +22,135 @@ namespace mozilla { +using webgl::QueueStatus; + +namespace webgl { + +struct ShmemCmdBuffer final { + mozilla::ipc::Shmem mShmem = {}; + size_t mPos = 0; + + Range Remaining() const { + const auto range = ByteRange(mShmem); + return {range.begin() + mPos, range.end()}; + } +}; + +// - + +class RangeConsumerView final : public webgl::ConsumerView { + RangedPtr mSrcItr; + const RangedPtr mSrcEnd; + + public: + auto Remaining() const { return *MaybeAs(mSrcEnd - mSrcItr); } + + explicit RangeConsumerView(const Range range) + : ConsumerView(this, nullptr, 0), + mSrcItr(range.begin()), + mSrcEnd(range.end()) { + (void)Remaining(); // assert size non-negative + } + + QueueStatus ReadObject(size_t*, size_t, void* const src, const size_t size) { + const auto remaining = Remaining(); + if (size > remaining) return QueueStatus::kTooSmall; + + memcpy(src, mSrcItr.get(), size); + mSrcItr += size; + return QueueStatus::kSuccess; + } +}; + +// - + +namespace details { + +class SizeOnlyProducerView final + : public webgl::ProducerView { + size_t mRequiredSize = 0; + + public: + SizeOnlyProducerView() : ProducerView(this, 0, nullptr) {} + + QueueStatus WriteObject(size_t, size_t*, const void*, const size_t size) { + mRequiredSize += size; + return QueueStatus::kSuccess; + } + + const auto& RequiredSize() const { return mRequiredSize; } +}; + +// - + +class RangeProducerView final : public webgl::ProducerView { + RangedPtr mDestItr; + const RangedPtr mDestEnd; + + public: + auto Remaining() const { return *MaybeAs(mDestEnd - mDestItr); } + + explicit RangeProducerView(const Range range) + : ProducerView(this, 0, nullptr), + mDestItr(range.begin()), + mDestEnd(range.end()) { + (void)Remaining(); // assert size non-negative + } + + QueueStatus WriteObject(size_t, size_t*, const void* const src, + const size_t size) { + MOZ_ASSERT(size <= Remaining()); + + memcpy(mDestItr.get(), src, size); + mDestItr += size; + return QueueStatus::kSuccess; + } +}; + +// - + +template +inline void Serialize(ProducerViewT&) {} + +template +inline void Serialize(ProducerViewT& view, const Arg& arg, + const Args&... args) { + MOZ_ALWAYS_TRUE(view.WriteParam(arg) == QueueStatus::kSuccess); + Serialize(view, args...); +} + +} // namespace details + +// - + +template +size_t SerializedSize(const Args&... args) { + webgl::details::SizeOnlyProducerView sizeView; + webgl::details::Serialize(sizeView, args...); + return sizeView.RequiredSize(); +} + +template +void Serialize(Range dest, const Args&... args) { + webgl::details::RangeProducerView view(dest); + webgl::details::Serialize(view, args...); +} + +// - + +inline bool Deserialize(RangeConsumerView& view) { return true; } + +template +inline bool Deserialize(RangeConsumerView& view, Arg& arg, Args&... args) { + if (!webgl::QueueParamTraits::Read(view, &arg)) return false; + return Deserialize(view, args...); +} + +} // namespace webgl + +// - + using mozilla::ipc::IPDLParamTraits; -using mozilla::webgl::QueueStatus; enum CommandResult { kSuccess, kTimeExpired, kQueueEmpty, kError }; @@ -528,41 +657,62 @@ class SyncCommandSink : public CommandSink { template