diff options
Diffstat (limited to 'gfx/ipc')
41 files changed, 6995 insertions, 0 deletions
diff --git a/gfx/ipc/CompositorOptions.h b/gfx/ipc/CompositorOptions.h new file mode 100644 index 0000000000..31b538b772 --- /dev/null +++ b/gfx/ipc/CompositorOptions.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_mozilla_gfx_ipc_CompositorOptions_h_ +#define _include_mozilla_gfx_ipc_CompositorOptions_h_ + +namespace IPC { +template <typename> +struct ParamTraits; +} // namespace IPC + +namespace mozilla { +namespace layers { + +/** + * This class holds options that are "per compositor" - that is, these options + * affect a particular CompositorBridgeParent and all the content that it + * renders. + * + * This class is intended to be created by a platform widget (but NOT + * PuppetWidget) and passed to the graphics code during initialization of the + * top level compositor associated with that widget. The options are immutable + * after creation. The CompositorBridgeParent holds the canonical version of + * the options, but they may be accessed by other parts of the code as needed, + * and are accessible to content processes over PCompositorBridge as well. + */ +class CompositorOptions { + public: + // This constructor needed for IPDL purposes, don't use it anywhere else. + CompositorOptions() + : mUseAPZ(false), + mUseWebRender(false), + mUseAdvancedLayers(false), + mInitiallyPaused(false) {} + + CompositorOptions(bool aUseAPZ, bool aUseWebRender) + : mUseAPZ(aUseAPZ), + mUseWebRender(aUseWebRender), + mUseAdvancedLayers(false), + mInitiallyPaused(false) {} + + bool UseAPZ() const { return mUseAPZ; } + bool UseWebRender() const { return mUseWebRender; } + bool UseAdvancedLayers() const { return mUseAdvancedLayers; } + bool InitiallyPaused() const { return mInitiallyPaused; } + + void SetUseAPZ(bool aUseAPZ) { mUseAPZ = aUseAPZ; } + + void SetUseAdvancedLayers(bool aUseAdvancedLayers) { + mUseAdvancedLayers = aUseAdvancedLayers; + } + + void SetInitiallyPaused(bool aPauseAtStartup) { + mInitiallyPaused = aPauseAtStartup; + } + + bool operator==(const CompositorOptions& aOther) const { + return mUseAPZ == aOther.mUseAPZ && mUseWebRender == aOther.mUseWebRender && + mUseAdvancedLayers == aOther.mUseAdvancedLayers; + } + + friend struct IPC::ParamTraits<CompositorOptions>; + + private: + bool mUseAPZ; + bool mUseWebRender; + bool mUseAdvancedLayers; + bool mInitiallyPaused; + + // Make sure to add new fields to the ParamTraits implementation + // in LayersMessageUtils.h +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_CompositorOptions_h_ diff --git a/gfx/ipc/CompositorSession.cpp b/gfx/ipc/CompositorSession.cpp new file mode 100644 index 0000000000..41c4955452 --- /dev/null +++ b/gfx/ipc/CompositorSession.cpp @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "CompositorSession.h" +#include "base/process_util.h" +#include "GPUChild.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/GPUProcessHost.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" + +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/widget/nsWindow.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +namespace mozilla { +namespace layers { + +using namespace gfx; +using namespace widget; + +CompositorSession::CompositorSession(nsBaseWidget* aWidget, + CompositorWidgetDelegate* aDelegate, + CompositorBridgeChild* aChild, + const LayersId& aRootLayerTreeId) + : mWidget(aWidget), + mCompositorWidgetDelegate(aDelegate), + mCompositorBridgeChild(aChild), + mRootLayerTreeId(aRootLayerTreeId) {} + +CompositorSession::~CompositorSession() = default; + +CompositorBridgeChild* CompositorSession::GetCompositorBridgeChild() { + return mCompositorBridgeChild; +} + +#if defined(MOZ_WIDGET_ANDROID) +void CompositorSession::NotifyDisablingWebRender() { + if (!mWidget) { + return; + } + nsWindow* window = static_cast<nsWindow*>(mWidget); + window->NotifyDisablingWebRender(); +} +#endif // defined(MOZ_WIDGET_ANDROID) + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/CompositorSession.h b/gfx/ipc/CompositorSession.h new file mode 100644 index 0000000000..d0b7f9131e --- /dev/null +++ b/gfx/ipc/CompositorSession.h @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_mozilla_gfx_ipc_CompositorSession_h_ +#define _include_mozilla_gfx_ipc_CompositorSession_h_ + +#include "base/basictypes.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/layers/CompositorTypes.h" +#include "nsISupportsImpl.h" +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/layers/UiCompositorControllerChild.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +class nsBaseWidget; + +namespace mozilla { +namespace widget { +class CompositorWidget; +class CompositorWidgetDelegate; +} // namespace widget +namespace gfx { +class GPUProcessHost; +class GPUProcessManager; +} // namespace gfx +namespace layers { + +class GeckoContentController; +class IAPZCTreeManager; +class CompositorBridgeParent; +class CompositorBridgeChild; +class ClientLayerManager; + +// A CompositorSession provides access to a compositor without exposing whether +// or not it's in-process or out-of-process. +class CompositorSession { + friend class gfx::GPUProcessManager; + + protected: + typedef gfx::GPUProcessHost GPUProcessHost; + typedef widget::CompositorWidget CompositorWidget; + typedef widget::CompositorWidgetDelegate CompositorWidgetDelegate; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorSession) + + virtual void Shutdown() = 0; + + // This returns a CompositorBridgeParent if the compositor resides in the same + // process. + virtual CompositorBridgeParent* GetInProcessBridge() const = 0; + + // Set the GeckoContentController for the root of the layer tree. + virtual void SetContentController(GeckoContentController* aController) = 0; + + // Return the Async Pan/Zoom Tree Manager for this compositor. + virtual RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const = 0; + + // Return the child end of the compositor IPC bridge. + CompositorBridgeChild* GetCompositorBridgeChild(); + + // Return the proxy for accessing the compositor's widget. + CompositorWidgetDelegate* GetCompositorWidgetDelegate() { + return mCompositorWidgetDelegate; + } + + // Return the id of the root layer tree. + LayersId RootLayerTreeId() const { return mRootLayerTreeId; } + +#if defined(MOZ_WIDGET_ANDROID) + // Set the UiCompositorControllerChild after Session creation so the Session + // constructor doesn't get mucked up for other platforms. + void SetUiCompositorControllerChild( + RefPtr<UiCompositorControllerChild> aUiController) { + mUiCompositorControllerChild = aUiController; + } + + RefPtr<UiCompositorControllerChild> GetUiCompositorControllerChild() { + return mUiCompositorControllerChild; + } + + void NotifyDisablingWebRender(); +#endif // defined(MOZ_WIDGET_ANDROID) + protected: + CompositorSession(nsBaseWidget* aWidget, CompositorWidgetDelegate* aDelegate, + CompositorBridgeChild* aChild, + const LayersId& aRootLayerTreeId); + virtual ~CompositorSession(); + + protected: + nsBaseWidget* mWidget; + CompositorWidgetDelegate* mCompositorWidgetDelegate; + RefPtr<CompositorBridgeChild> mCompositorBridgeChild; + LayersId mRootLayerTreeId; +#if defined(MOZ_WIDGET_ANDROID) + RefPtr<UiCompositorControllerChild> mUiCompositorControllerChild; +#endif // defined(MOZ_WIDGET_ANDROID) + private: + DISALLOW_COPY_AND_ASSIGN(CompositorSession); +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_CompositorSession_h_ diff --git a/gfx/ipc/CompositorWidgetVsyncObserver.cpp b/gfx/ipc/CompositorWidgetVsyncObserver.cpp new file mode 100644 index 0000000000..d149169050 --- /dev/null +++ b/gfx/ipc/CompositorWidgetVsyncObserver.cpp @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CompositorWidgetVsyncObserver.h" +#include "mozilla/gfx/VsyncBridgeChild.h" + +namespace mozilla { +namespace widget { + +CompositorWidgetVsyncObserver::CompositorWidgetVsyncObserver( + RefPtr<VsyncBridgeChild> aVsyncBridge, + const layers::LayersId& aRootLayerTreeId) + : mVsyncBridge(aVsyncBridge), mRootLayerTreeId(aRootLayerTreeId) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); +} + +bool CompositorWidgetVsyncObserver::NotifyVsync(const VsyncEvent& aVsync) { + // Vsync notifications should only arrive on the vsync thread. + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + + mVsyncBridge->NotifyVsync(aVsync, mRootLayerTreeId); + return true; +} + +} // namespace widget +} // namespace mozilla diff --git a/gfx/ipc/CompositorWidgetVsyncObserver.h b/gfx/ipc/CompositorWidgetVsyncObserver.h new file mode 100644 index 0000000000..9318c96696 --- /dev/null +++ b/gfx/ipc/CompositorWidgetVsyncObserver.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h +#define mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h + +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/VsyncDispatcher.h" + +namespace mozilla { +namespace gfx { +class VsyncBridgeChild; +} // namespace gfx + +namespace widget { + +class CompositorWidgetVsyncObserver : public VsyncObserver { + typedef gfx::VsyncBridgeChild VsyncBridgeChild; + + public: + CompositorWidgetVsyncObserver(RefPtr<VsyncBridgeChild> aVsyncBridge, + const layers::LayersId& aRootLayerTreeId); + + bool NotifyVsync(const VsyncEvent& aVsync) override; + + private: + RefPtr<VsyncBridgeChild> mVsyncBridge; + layers::LayersId mRootLayerTreeId; +}; + +} // namespace widget +} // namespace mozilla + +#endif // mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h diff --git a/gfx/ipc/CrossProcessPaint.cpp b/gfx/ipc/CrossProcessPaint.cpp new file mode 100644 index 0000000000..143cd882d4 --- /dev/null +++ b/gfx/ipc/CrossProcessPaint.cpp @@ -0,0 +1,456 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CrossProcessPaint.h" + +#include "mozilla/dom/ContentProcessManager.h" +#include "mozilla/dom/ImageBitmap.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/PWindowGlobalParent.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/dom/WindowGlobalChild.h" +#include "mozilla/dom/WindowGlobalActorsBinding.h" +#include "mozilla/gfx/DrawEventRecorder.h" +#include "mozilla/gfx/InlineTranslator.h" +#include "mozilla/Logging.h" +#include "mozilla/PresShell.h" + +#include "gfxPlatform.h" + +#include "nsContentUtils.h" +#include "nsGlobalWindowInner.h" +#include "nsIDocShell.h" +#include "nsPresContext.h" + +static mozilla::LazyLogModule gCrossProcessPaintLog("CrossProcessPaint"); +static mozilla::LazyLogModule gPaintFragmentLog("PaintFragment"); + +#define CPP_LOG(msg, ...) \ + MOZ_LOG(gCrossProcessPaintLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) +#define PF_LOG(msg, ...) \ + MOZ_LOG(gPaintFragmentLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) + +namespace mozilla { +namespace gfx { + +using namespace mozilla::ipc; + +/// The minimum scale we allow tabs to be rasterized at. +static const float kMinPaintScale = 0.05f; + +/* static */ +PaintFragment PaintFragment::Record(dom::BrowsingContext* aBc, + const Maybe<IntRect>& aRect, float aScale, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags) { + nsIDocShell* ds = aBc->GetDocShell(); + if (!ds) { + PF_LOG("Couldn't find docshell.\n"); + return PaintFragment{}; + } + + RefPtr<nsPresContext> presContext = ds->GetPresContext(); + if (!presContext) { + PF_LOG("Couldn't find PresContext.\n"); + return PaintFragment{}; + } + + IntRect rect; + if (!aRect) { + nsCOMPtr<nsIWidget> widget = + nsContentUtils::WidgetForDocument(presContext->Document()); + + // TODO: Apply some sort of clipping to visible bounds here (Bug 1562720) + LayoutDeviceIntRect boundsDevice = widget->GetBounds(); + boundsDevice.MoveTo(0, 0); + nsRect boundsAu = LayoutDevicePixel::ToAppUnits( + boundsDevice, presContext->AppUnitsPerDevPixel()); + rect = gfx::RoundedOut(CSSPixel::FromAppUnits(boundsAu).ToUnknownRect()); + } else { + rect = *aRect; + } + + if (rect.IsEmpty()) { + // TODO: Should we return an empty surface here? + PF_LOG("Empty rect to paint.\n"); + return PaintFragment{}; + } + + IntSize surfaceSize = rect.Size(); + surfaceSize.width *= aScale; + surfaceSize.height *= aScale; + + CPP_LOG( + "Recording " + "[browsingContext=%p, " + "rect=(%d, %d) x (%d, %d), " + "scale=%f, " + "color=(%u, %u, %u, %u)]\n", + aBc, rect.x, rect.y, rect.width, rect.height, aScale, + NS_GET_R(aBackgroundColor), NS_GET_G(aBackgroundColor), + NS_GET_B(aBackgroundColor), NS_GET_A(aBackgroundColor)); + + // Check for invalid sizes + if (surfaceSize.width <= 0 || surfaceSize.height <= 0 || + !Factory::CheckSurfaceSize(surfaceSize)) { + PF_LOG("Invalid surface size of (%d x %d).\n", surfaceSize.width, + surfaceSize.height); + return PaintFragment{}; + } + + // Flush any pending notifications + nsContentUtils::FlushLayoutForTree(ds->GetWindow()); + + // Initialize the recorder + SurfaceFormat format = SurfaceFormat::B8G8R8A8; + RefPtr<DrawTarget> referenceDt = Factory::CreateDrawTarget( + gfxPlatform::GetPlatform()->GetSoftwareBackend(), IntSize(1, 1), format); + + // TODO: This may OOM crash if the content is complex enough + RefPtr<DrawEventRecorderMemory> recorder = + MakeAndAddRef<DrawEventRecorderMemory>(nullptr); + RefPtr<DrawTarget> dt = Factory::CreateRecordingDrawTarget( + recorder, referenceDt, IntRect(IntPoint(0, 0), surfaceSize)); + + RenderDocumentFlags renderDocFlags = RenderDocumentFlags::None; + if (!(aFlags & CrossProcessPaintFlags::DrawView)) { + renderDocFlags = (RenderDocumentFlags::IgnoreViewportScrolling | + RenderDocumentFlags::DocumentRelative); + } + + // Perform the actual rendering + { + nsRect r(nsPresContext::CSSPixelsToAppUnits(rect.x), + nsPresContext::CSSPixelsToAppUnits(rect.y), + nsPresContext::CSSPixelsToAppUnits(rect.width), + nsPresContext::CSSPixelsToAppUnits(rect.height)); + + RefPtr<gfxContext> thebes = gfxContext::CreateOrNull(dt); + thebes->SetMatrix(Matrix::Scaling(aScale, aScale)); + RefPtr<PresShell> presShell = presContext->PresShell(); + Unused << presShell->RenderDocument(r, renderDocFlags, aBackgroundColor, + thebes); + } + + if (!recorder->mOutputStream.mValid) { + return PaintFragment{}; + } + + ByteBuf recording = ByteBuf((uint8_t*)recorder->mOutputStream.mData, + recorder->mOutputStream.mLength, + recorder->mOutputStream.mCapacity); + recorder->mOutputStream.mData = nullptr; + recorder->mOutputStream.mLength = 0; + recorder->mOutputStream.mCapacity = 0; + + return PaintFragment{ + surfaceSize, + std::move(recording), + std::move(recorder->TakeDependentSurfaces()), + }; +} + +bool PaintFragment::IsEmpty() const { + return !mRecording.mData || mRecording.mLen == 0 || mSize == IntSize(0, 0); +} + +PaintFragment::PaintFragment(IntSize aSize, ByteBuf&& aRecording, + nsTHashtable<nsUint64HashKey>&& aDependencies) + : mSize(aSize), + mRecording(std::move(aRecording)), + mDependencies(std::move(aDependencies)) {} + +static dom::TabId GetTabId(dom::WindowGlobalParent* aWGP) { + // There is no unique TabId for a given WindowGlobalParent, as multiple + // WindowGlobalParents share the same PBrowser actor. However, we only + // ever queue one paint per PBrowser by just using the current + // WindowGlobalParent for a PBrowser. So we can interchange TabId and + // WindowGlobalParent when dealing with resolving surfaces. + RefPtr<dom::BrowserParent> browserParent = aWGP->GetBrowserParent(); + return browserParent ? browserParent->GetTabId() : dom::TabId(0); +} + +/* static */ +bool CrossProcessPaint::Start(dom::WindowGlobalParent* aRoot, + const dom::DOMRect* aRect, float aScale, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags, + dom::Promise* aPromise) { + MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); + aScale = std::max(aScale, kMinPaintScale); + + CPP_LOG( + "Starting paint. " + "[wgp=%p, " + "scale=%f, " + "color=(%u, %u, %u, %u)]\n", + aRoot, aScale, NS_GET_R(aBackgroundColor), NS_GET_G(aBackgroundColor), + NS_GET_B(aBackgroundColor), NS_GET_A(aBackgroundColor)); + + Maybe<IntRect> rect; + if (aRect) { + rect = + Some(IntRect::RoundOut((float)aRect->X(), (float)aRect->Y(), + (float)aRect->Width(), (float)aRect->Height())); + } + + if (rect && rect->IsEmpty()) { + return false; + } + + dom::TabId rootId = GetTabId(aRoot); + + RefPtr<CrossProcessPaint> resolver = new CrossProcessPaint(aScale, rootId); + RefPtr<CrossProcessPaint::ResolvePromise> promise; + + if (aRoot->IsInProcess()) { + RefPtr<dom::WindowGlobalChild> childActor = aRoot->GetChildActor(); + if (!childActor) { + return false; + } + + // `BrowsingContext()` cannot be nullptr. + RefPtr<dom::BrowsingContext> bc = childActor->BrowsingContext(); + + promise = resolver->Init(); + resolver->mPendingFragments += 1; + resolver->ReceiveFragment( + aRoot, + PaintFragment::Record(bc, rect, aScale, aBackgroundColor, aFlags)); + } else { + promise = resolver->Init(); + resolver->QueuePaint(aRoot, rect, aBackgroundColor, aFlags); + } + + promise->Then( + GetCurrentSerialEventTarget(), __func__, + [promise = RefPtr{aPromise}, rootId](ResolvedFragmentMap&& aFragments) { + RefPtr<RecordedDependentSurface> root = aFragments.Get(rootId); + CPP_LOG("Resolved all fragments.\n"); + + // Create the destination draw target + RefPtr<DrawTarget> drawTarget = + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget( + root->mSize, SurfaceFormat::B8G8R8A8); + if (!drawTarget || !drawTarget->IsValid()) { + CPP_LOG("Couldn't create (%d x %d) surface for fragment %" PRIu64 + ".\n", + root->mSize.width, root->mSize.height, (uint64_t)rootId); + promise->MaybeReject(NS_ERROR_FAILURE); + return; + } + + // Translate the recording using our child tabs + { + InlineTranslator translator(drawTarget, nullptr); + translator.SetDependentSurfaces(&aFragments); + if (!translator.TranslateRecording((char*)root->mRecording.mData, + root->mRecording.mLen)) { + CPP_LOG("Couldn't translate recording for fragment %" PRIu64 ".\n", + (uint64_t)rootId); + promise->MaybeReject(NS_ERROR_FAILURE); + return; + } + } + + RefPtr<SourceSurface> snapshot = drawTarget->Snapshot(); + if (!snapshot) { + promise->MaybeReject(NS_ERROR_FAILURE); + return; + } + + ErrorResult rv; + RefPtr<dom::ImageBitmap> bitmap = + dom::ImageBitmap::CreateFromSourceSurface( + promise->GetParentObject(), snapshot, rv); + + if (!rv.Failed()) { + CPP_LOG("Success, fulfilling promise.\n"); + promise->MaybeResolve(bitmap); + } else { + CPP_LOG("Couldn't create ImageBitmap for SourceSurface.\n"); + promise->MaybeReject(std::move(rv)); + } + }, + [promise = RefPtr{aPromise}](const nsresult& aRv) { + promise->MaybeReject(aRv); + }); + + return true; +} + +/* static */ +RefPtr<CrossProcessPaint::ResolvePromise> CrossProcessPaint::Start( + nsTHashtable<nsUint64HashKey>&& aDependencies) { + MOZ_ASSERT(!aDependencies.IsEmpty()); + RefPtr<CrossProcessPaint> resolver = + new CrossProcessPaint(1.0, dom::TabId(0)); + + RefPtr<CrossProcessPaint::ResolvePromise> promise = resolver->Init(); + + PaintFragment rootFragment; + rootFragment.mDependencies = std::move(aDependencies); + + resolver->QueueDependencies(rootFragment.mDependencies); + resolver->mReceivedFragments.Put(dom::TabId(0), std::move(rootFragment)); + + return promise; +} + +CrossProcessPaint::CrossProcessPaint(float aScale, dom::TabId aRoot) + : mRoot{aRoot}, mScale{aScale}, mPendingFragments{0} {} + +CrossProcessPaint::~CrossProcessPaint() = default; + +void CrossProcessPaint::ReceiveFragment(dom::WindowGlobalParent* aWGP, + PaintFragment&& aFragment) { + if (IsCleared()) { + CPP_LOG("Ignoring fragment from %p.\n", aWGP); + return; + } + + dom::TabId surfaceId = GetTabId(aWGP); + + MOZ_ASSERT(mPendingFragments > 0); + MOZ_ASSERT(!mReceivedFragments.GetValue(surfaceId)); + + // Double check our invariants to protect against a compromised content + // process + if (mPendingFragments == 0 || mReceivedFragments.GetValue(surfaceId) || + aFragment.IsEmpty()) { + CPP_LOG("Dropping invalid fragment from %p.\n", aWGP); + LostFragment(aWGP); + return; + } + + CPP_LOG("Receiving fragment from %p(%" PRIu64 ").\n", aWGP, + (uint64_t)surfaceId); + + // Queue paints for child tabs + QueueDependencies(aFragment.mDependencies); + + mReceivedFragments.Put(surfaceId, std::move(aFragment)); + mPendingFragments -= 1; + + // Resolve this paint if we have received all pending fragments + MaybeResolve(); +} + +void CrossProcessPaint::LostFragment(dom::WindowGlobalParent* aWGP) { + if (IsCleared()) { + CPP_LOG("Ignoring lost fragment from %p.\n", aWGP); + return; + } + + Clear(NS_ERROR_LOSS_OF_SIGNIFICANT_DATA); +} + +void CrossProcessPaint::QueueDependencies( + const nsTHashtable<nsUint64HashKey>& aDependencies) { + for (auto iter = aDependencies.ConstIter(); !iter.Done(); iter.Next()) { + auto dependency = dom::TabId(iter.Get()->GetKey()); + + // Get the current WindowGlobalParent of the remote browser that was marked + // as a dependency + dom::ContentProcessManager* cpm = + dom::ContentProcessManager::GetSingleton(); + dom::ContentParentId cpId = cpm->GetTabProcessId(dependency); + RefPtr<dom::BrowserParent> browser = + cpm->GetBrowserParentByProcessAndTabId(cpId, dependency); + RefPtr<dom::WindowGlobalParent> wgp = + browser->GetBrowsingContext()->GetCurrentWindowGlobal(); + + if (!wgp) { + CPP_LOG("Skipping dependency %" PRIu64 "with no current WGP.\n", + (uint64_t)dependency); + continue; + } + + // TODO: Apply some sort of clipping to visible bounds here (Bug 1562720) + QueuePaint(wgp, Nothing()); + } +} + +void CrossProcessPaint::QueuePaint(dom::WindowGlobalParent* aWGP, + const Maybe<IntRect>& aRect, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags) { + MOZ_ASSERT(!mReceivedFragments.GetValue(GetTabId(aWGP))); + + CPP_LOG("Queueing paint for %p.\n", aWGP); + + aWGP->DrawSnapshotInternal(this, aRect, mScale, aBackgroundColor, + (uint32_t)aFlags); + mPendingFragments += 1; +} + +void CrossProcessPaint::Clear(nsresult aStatus) { + mPendingFragments = 0; + mReceivedFragments.Clear(); + mPromise.RejectIfExists(aStatus, __func__); +} + +bool CrossProcessPaint::IsCleared() const { return mPromise.IsEmpty(); } + +void CrossProcessPaint::MaybeResolve() { + // Don't do anything if we aren't ready, experienced an error, or already + // resolved this paint + if (IsCleared() || mPendingFragments > 0) { + CPP_LOG("Not ready to resolve yet, have %u fragments left.\n", + mPendingFragments); + return; + } + + CPP_LOG("Starting to resolve fragments.\n"); + + // Resolve the paint fragments from the bottom up + ResolvedFragmentMap resolved; + { + nsresult rv = ResolveInternal(mRoot, &resolved); + if (NS_FAILED(rv)) { + CPP_LOG("Couldn't resolve.\n"); + Clear(rv); + return; + } + } + + CPP_LOG("Resolved all fragments.\n"); + + mPromise.ResolveIfExists(std::move(resolved), __func__); + Clear(NS_OK); +} + +nsresult CrossProcessPaint::ResolveInternal(dom::TabId aTabId, + ResolvedFragmentMap* aResolved) { + // We should not have resolved this paint already + MOZ_ASSERT(!aResolved->GetWeak(aTabId)); + + CPP_LOG("Resolving fragment %" PRIu64 ".\n", (uint64_t)aTabId); + + Maybe<PaintFragment> fragment = mReceivedFragments.GetAndRemove(aTabId); + if (!fragment) { + return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; + } + + // Rasterize all the dependencies first so that we can resolve this fragment + for (auto iter = fragment->mDependencies.Iter(); !iter.Done(); iter.Next()) { + auto dependency = dom::TabId(iter.Get()->GetKey()); + + nsresult rv = ResolveInternal(dependency, aResolved); + if (NS_FAILED(rv)) { + return rv; + } + } + + RefPtr<RecordedDependentSurface> surface = new RecordedDependentSurface{ + fragment->mSize, std::move(fragment->mRecording)}; + aResolved->Put(aTabId, std::move(surface)); + return NS_OK; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/CrossProcessPaint.h b/gfx/ipc/CrossProcessPaint.h new file mode 100644 index 0000000000..8179b49b41 --- /dev/null +++ b/gfx/ipc/CrossProcessPaint.h @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_mozilla_gfx_ipc_CrossProcessPaint_h_ +#define _include_mozilla_gfx_ipc_CrossProcessPaint_h_ + +#include "nsISupportsImpl.h" + +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/gfx/RecordedEvent.h" +#include "mozilla/gfx/Rect.h" +#include "mozilla/MozPromise.h" +#include "mozilla/ipc/ByteBuf.h" +#include "nsColor.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsRefPtrHashtable.h" +#include "nsTHashtable.h" + +class nsIDocShell; + +namespace IPC { +template <typename T> +struct ParamTraits; +} // namespace IPC + +namespace mozilla { + +namespace dom { +class DOMRect; +class Promise; +class WindowGlobalParent; +} // namespace dom + +namespace gfx { + +class CrossProcessPaint; + +enum class CrossProcessPaintFlags { + None = 0, + DrawView = 1 << 1, +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CrossProcessPaintFlags) + +/** + * A fragment of a paint of a cross process document tree. + */ +class PaintFragment final { + public: + /// Initializes an empty PaintFragment + PaintFragment() = default; + + /** + * Creates a paint fragment by recording the draw commands and dependent tabs + * for a BrowsingContext. + * + * @param aBrowsingContext The frame to record. + * @param aRect The rectangle relative to the viewport to use. If no + * rectangle is specified, then the whole viewport will be used. + * @param aScale The coordinate scale to use. The size of the resolved + * surface will be `aRect.Size() * aScale`, with aScale clamped to + * at least kMinPaintScale. + * @param aBackgroundColor The background color to use. + * + * @return A paint fragment. The paint fragment may be `empty` if rendering + * was unable to be accomplished for some reason. + */ + static PaintFragment Record(dom::BrowsingContext* aBc, + const Maybe<IntRect>& aRect, float aScale, + nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags); + + /// Returns whether this paint fragment contains a valid recording. + bool IsEmpty() const; + + PaintFragment(PaintFragment&&) = default; + PaintFragment& operator=(PaintFragment&&) = default; + + protected: + friend struct mozilla::ipc::IPDLParamTraits<PaintFragment>; + friend CrossProcessPaint; + + typedef mozilla::ipc::ByteBuf ByteBuf; + + PaintFragment(IntSize, ByteBuf&&, nsTHashtable<nsUint64HashKey>&&); + + IntSize mSize; + ByteBuf mRecording; + nsTHashtable<nsUint64HashKey> mDependencies; +}; + +/** + * An object for painting a cross process document tree. + */ +class CrossProcessPaint final { + NS_INLINE_DECL_REFCOUNTING(CrossProcessPaint); + + public: + typedef nsRefPtrHashtable<nsUint64HashKey, RecordedDependentSurface> + ResolvedFragmentMap; + typedef MozPromise<ResolvedFragmentMap, nsresult, true> ResolvePromise; + /** + * Begin an asynchronous paint of a cross process document tree starting at + * a WindowGlobalParent. A maybe-async paint for the root WGP will be done, + * then async paints will be recursively queued for remote subframes. Once + * all subframes have been recorded, the final image will be resolved, and + * the promise will be resolved with a dom::ImageBitmap. + * + * @param aRoot The WindowGlobalParent to paint. + * @param aRect The rectangle relative to the viewport to use, or null to + * render the whole viewport. + * @param aScale The coordinate scale to use. The size of the resolved + * surface will be `aRect.Size() * aScale`, with aScale clamped to + * at least kMinPaintScale. See the implementation for the current + * minimum value. + * @param aBackgroundColor The background color to use. + * @param aPromise The promise to resolve with a dom::ImageBitmap. + * + * @returns Whether the paint was able to be initiated or not. + */ + static bool Start(dom::WindowGlobalParent* aRoot, const dom::DOMRect* aRect, + float aScale, nscolor aBackgroundColor, + CrossProcessPaintFlags aFlags, dom::Promise* aPromise); + + static RefPtr<ResolvePromise> Start( + nsTHashtable<nsUint64HashKey>&& aDependencies); + + void ReceiveFragment(dom::WindowGlobalParent* aWGP, + PaintFragment&& aFragment); + void LostFragment(dom::WindowGlobalParent* aWGP); + + private: + typedef nsDataHashtable<nsUint64HashKey, PaintFragment> ReceivedFragmentMap; + + CrossProcessPaint(float aScale, dom::TabId aRoot); + ~CrossProcessPaint(); + + void QueueDependencies(const nsTHashtable<nsUint64HashKey>& aDependencies); + + void QueuePaint( + dom::WindowGlobalParent* aWGP, const Maybe<IntRect>& aRect, + nscolor aBackgroundColor = NS_RGBA(0, 0, 0, 0), + CrossProcessPaintFlags aFlags = CrossProcessPaintFlags::DrawView); + + /// Clear the state of this paint so that it cannot be resolved or receive + /// any paint fragments. + void Clear(nsresult aStatus); + + /// Returns if this paint has been cleared. + bool IsCleared() const; + + /// Resolves the paint fragments if we have none pending and resolves the + /// promise. + void MaybeResolve(); + nsresult ResolveInternal(dom::TabId aTabId, ResolvedFragmentMap* aResolved); + + RefPtr<ResolvePromise> Init() { + MOZ_ASSERT(mPromise.IsEmpty()); + return mPromise.Ensure(__func__); + } + + MozPromiseHolder<ResolvePromise> mPromise; + dom::TabId mRoot; + float mScale; + uint32_t mPendingFragments; + ReceivedFragmentMap mReceivedFragments; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_CrossProcessPaint_h_ diff --git a/gfx/ipc/D3DMessageUtils.cpp b/gfx/ipc/D3DMessageUtils.cpp new file mode 100644 index 0000000000..11c615ed03 --- /dev/null +++ b/gfx/ipc/D3DMessageUtils.cpp @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "D3DMessageUtils.h" +#if defined(XP_WIN) +# include "gfxWindowsPlatform.h" +#endif + +bool DxgiAdapterDesc::operator==(const DxgiAdapterDesc& aOther) const { + return memcmp(&aOther, this, sizeof(*this)) == 0; +} + +#if defined(XP_WIN) +static_assert(sizeof(DxgiAdapterDesc) == sizeof(DXGI_ADAPTER_DESC), + "DXGI_ADAPTER_DESC doe snot match DxgiAdapterDesc"); + +const DxgiAdapterDesc& DxgiAdapterDesc::From(const DXGI_ADAPTER_DESC& aDesc) { + return reinterpret_cast<const DxgiAdapterDesc&>(aDesc); +} + +const DXGI_ADAPTER_DESC& DxgiAdapterDesc::ToDesc() const { + return reinterpret_cast<const DXGI_ADAPTER_DESC&>(*this); +} +#endif + +namespace IPC { + +void ParamTraits<DxgiAdapterDesc>::Write(Message* aMsg, + const paramType& aParam) { +#if defined(XP_WIN) + aMsg->WriteBytes(aParam.Description, sizeof(aParam.Description)); + WriteParam(aMsg, aParam.VendorId); + WriteParam(aMsg, aParam.DeviceId); + WriteParam(aMsg, aParam.SubSysId); + WriteParam(aMsg, aParam.Revision); + WriteParam(aMsg, aParam.DedicatedVideoMemory); + WriteParam(aMsg, aParam.DedicatedSystemMemory); + WriteParam(aMsg, aParam.SharedSystemMemory); + WriteParam(aMsg, aParam.AdapterLuid.LowPart); + WriteParam(aMsg, aParam.AdapterLuid.HighPart); +#endif +} + +bool ParamTraits<DxgiAdapterDesc>::Read(const Message* aMsg, + PickleIterator* aIter, + paramType* aResult) { +#if defined(XP_WIN) + if (!aMsg->ReadBytesInto(aIter, aResult->Description, + sizeof(aResult->Description))) { + return false; + } + + if (ReadParam(aMsg, aIter, &aResult->VendorId) && + ReadParam(aMsg, aIter, &aResult->DeviceId) && + ReadParam(aMsg, aIter, &aResult->SubSysId) && + ReadParam(aMsg, aIter, &aResult->Revision) && + ReadParam(aMsg, aIter, &aResult->DedicatedVideoMemory) && + ReadParam(aMsg, aIter, &aResult->DedicatedSystemMemory) && + ReadParam(aMsg, aIter, &aResult->SharedSystemMemory) && + ReadParam(aMsg, aIter, &aResult->AdapterLuid.LowPart) && + ReadParam(aMsg, aIter, &aResult->AdapterLuid.HighPart)) { + return true; + } + return false; +#else + return true; +#endif +} + +} // namespace IPC diff --git a/gfx/ipc/D3DMessageUtils.h b/gfx/ipc/D3DMessageUtils.h new file mode 100644 index 0000000000..ef10785e15 --- /dev/null +++ b/gfx/ipc/D3DMessageUtils.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_gfx_ipc_D3DMessageUtils_h__ +#define _include_gfx_ipc_D3DMessageUtils_h__ + +#include "chrome/common/ipc_message_utils.h" +#include "ipc/IPCMessageUtils.h" + +// Can't include dxgi.h, since it leaks random identifiers and #defines, and +// IPDL causes this file to be #included all over. +typedef struct DXGI_ADAPTER_DESC DXGI_ADAPTER_DESC; + +struct DxgiAdapterDesc { +#if defined(XP_WIN) + WCHAR Description[128]; + UINT VendorId; + UINT DeviceId; + UINT SubSysId; + UINT Revision; + SIZE_T DedicatedVideoMemory; + SIZE_T DedicatedSystemMemory; + SIZE_T SharedSystemMemory; + LUID AdapterLuid; + + static const DxgiAdapterDesc& From(const DXGI_ADAPTER_DESC& aDesc); + const DXGI_ADAPTER_DESC& ToDesc() const; +#endif + + bool operator==(const DxgiAdapterDesc& aOther) const; +}; + +namespace IPC { + +template <> +struct ParamTraits<DxgiAdapterDesc> { + typedef DxgiAdapterDesc paramType; + static void Write(Message* aMsg, const paramType& aParam); + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult); +}; + +} // namespace IPC + +#endif // _include_gfx_ipc_D3DMessageUtils_h__ diff --git a/gfx/ipc/GPUChild.cpp b/gfx/ipc/GPUChild.cpp new file mode 100644 index 0000000000..909d0aad81 --- /dev/null +++ b/gfx/ipc/GPUChild.cpp @@ -0,0 +1,329 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "GPUChild.h" + +#include "GPUProcessHost.h" +#include "GPUProcessManager.h" +#include "GfxInfoBase.h" +#include "VRProcessManager.h" +#include "gfxConfig.h" +#include "gfxPlatform.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TelemetryIPC.h" +#include "mozilla/dom/CheckerboardReportService.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/MemoryReportRequest.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/gfxVars.h" +#if defined(XP_WIN) +# include "mozilla/gfx/DeviceManagerDx.h" +#endif +#include "mozilla/HangDetails.h" +#include "mozilla/RemoteDecoderManagerChild.h" // For RemoteDecodeIn +#include "mozilla/Unused.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/APZInputBridgeChild.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "nsIGfxInfo.h" +#include "nsIObserverService.h" + +#ifdef MOZ_GECKO_PROFILER +# include "ProfilerParent.h" +#endif + +namespace mozilla { +namespace gfx { + +using namespace layers; + +GPUChild::GPUChild(GPUProcessHost* aHost) : mHost(aHost), mGPUReady(false) { + MOZ_COUNT_CTOR(GPUChild); +} + +GPUChild::~GPUChild() { MOZ_COUNT_DTOR(GPUChild); } + +void GPUChild::Init() { + nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars(); + + DevicePrefs devicePrefs; + devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING); + devicePrefs.d3d11Compositing() = + gfxConfig::GetValue(Feature::D3D11_COMPOSITING); + devicePrefs.oglCompositing() = + gfxConfig::GetValue(Feature::OPENGL_COMPOSITING); + devicePrefs.advancedLayers() = gfxConfig::GetValue(Feature::ADVANCED_LAYERS); + devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D); + devicePrefs.webGPU() = gfxConfig::GetValue(Feature::WEBGPU); + devicePrefs.d3d11HwAngle() = gfxConfig::GetValue(Feature::D3D11_HW_ANGLE); + + nsTArray<LayerTreeIdMapping> mappings; + LayerTreeOwnerTracker::Get()->Iterate( + [&](LayersId aLayersId, base::ProcessId aProcessId) { + mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId)); + }); + + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + nsTArray<GfxInfoFeatureStatus> features; + if (gfxInfo) { + auto* gfxInfoRaw = static_cast<widget::GfxInfoBase*>(gfxInfo.get()); + features = gfxInfoRaw->GetAllFeatures(); + } + + SendInit(updates, devicePrefs, mappings, features); + + gfxVars::AddReceiver(this); + +#ifdef MOZ_GECKO_PROFILER + Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid())); +#endif +} + +void GPUChild::OnVarChanged(const GfxVarUpdate& aVar) { SendUpdateVar(aVar); } + +bool GPUChild::EnsureGPUReady() { + if (mGPUReady) { + return true; + } + + GPUDeviceData data; + if (!SendGetDeviceStatus(&data)) { + return false; + } + + gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, + mHost->GetLaunchTime()); + mGPUReady = true; + return true; +} + +base::ProcessHandle GPUChild::GetChildProcessHandle() { + return mHost->GetChildProcessHandle(); +} + +mozilla::ipc::IPCResult GPUChild::RecvInitComplete(const GPUDeviceData& aData) { + // We synchronously requested GPU parameters before this arrived. + if (mGPUReady) { + return IPC_OK(); + } + + gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, + mHost->GetLaunchTime()); + mGPUReady = true; + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvReportCheckerboard( + const uint32_t& aSeverity, const nsCString& aLog) { + layers::CheckerboardEventStorage::Report(aSeverity, std::string(aLog.get())); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvGraphicsError(const nsCString& aError) { + gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder(); + if (lf) { + std::stringstream message; + message << "GP+" << aError.get(); + lf->UpdateStringsVector(message.str()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvCreateVRProcess() { + // Make sure create VR process at the main process + MOZ_ASSERT(XRE_IsParentProcess()); + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRProcessManager::Initialize(); + VRProcessManager* vr = VRProcessManager::Get(); + MOZ_ASSERT(vr, "VRProcessManager must be initialized first."); + + if (vr) { + vr->LaunchVRProcess(); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvShutdownVRProcess() { + // Make sure stopping VR process at the main process + MOZ_ASSERT(XRE_IsParentProcess()); + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRProcessManager::Shutdown(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyUiObservers( + const nsCString& aTopic) { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildHistograms( + nsTArray<HistogramAccumulation>&& aAccumulations) { + TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Gpu, + aAccumulations); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvAccumulateChildKeyedHistograms( + nsTArray<KeyedHistogramAccumulation>&& aAccumulations) { + TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Gpu, + aAccumulations); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateChildScalars( + nsTArray<ScalarAction>&& aScalarActions) { + TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Gpu, aScalarActions); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateChildKeyedScalars( + nsTArray<KeyedScalarAction>&& aScalarActions) { + TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Gpu, + aScalarActions); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvRecordChildEvents( + nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) { + TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Gpu, aEvents); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvRecordDiscardedData( + const mozilla::Telemetry::DiscardedData& aDiscardedData) { + TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Gpu, aDiscardedData); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvNotifyDeviceReset( + const GPUDeviceData& aData) { + gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); + mHost->mListener->OnRemoteProcessDeviceReset(mHost); + return IPC_OK(); +} + +bool GPUChild::SendRequestMemoryReport(const uint32_t& aGeneration, + const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const Maybe<FileDescriptor>& aDMDFile) { + mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration); + + PGPUChild::SendRequestMemoryReport( + aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, + [&](const uint32_t& aGeneration2) { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + if (child->mMemoryReportRequest) { + child->mMemoryReportRequest->Finish(aGeneration2); + child->mMemoryReportRequest = nullptr; + } + } + } + }, + [&](mozilla::ipc::ResponseRejectReason) { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + child->mMemoryReportRequest = nullptr; + } + } + }); + + return true; +} + +mozilla::ipc::IPCResult GPUChild::RecvAddMemoryReport( + const MemoryReport& aReport) { + if (mMemoryReportRequest) { + mMemoryReportRequest->RecvReport(aReport); + } + return IPC_OK(); +} + +void GPUChild::ActorDestroy(ActorDestroyReason aWhy) { + if (aWhy == AbnormalShutdown) { + GenerateCrashReport(OtherPid()); + + Telemetry::Accumulate( + Telemetry::SUBPROCESS_ABNORMAL_ABORT, + nsDependentCString(XRE_GeckoProcessTypeToString(GeckoProcessType_GPU)), + 1); + + // Notify the Telemetry environment so that we can refresh and do a + // subsession split + if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) { + obsvc->NotifyObservers(nullptr, "compositor:process-aborted", nullptr); + } + } + + gfxVars::RemoveReceiver(this); + mHost->OnChannelClosed(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateFeature( + const Feature& aFeature, const FeatureFailure& aChange) { + gfxConfig::SetFailed(aFeature, aChange.status(), aChange.message().get(), + aChange.failureId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUsedFallback(const Fallback& aFallback, + const nsCString& aMessage) { + gfxConfig::EnableFallback(aFallback, aMessage.get()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvBHRThreadHang( + const HangDetails& aDetails) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + // Copy the HangDetails recieved over the network into a nsIHangDetails, and + // then fire our own observer notification. + // XXX: We should be able to avoid this potentially expensive copy here by + // moving our deserialized argument. + nsCOMPtr<nsIHangDetails> hangDetails = + new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No); + obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUChild::RecvUpdateMediaCodecsSupported( + const PDMFactory::MediaCodecsSupported& aSupported) { + dom::ContentParent::BroadcastMediaCodecsSupportedUpdate( + RemoteDecodeIn::GpuProcess, aSupported); + return IPC_OK(); +} + +class DeferredDeleteGPUChild : public Runnable { + public: + explicit DeferredDeleteGPUChild(UniquePtr<GPUChild>&& aChild) + : Runnable("gfx::DeferredDeleteGPUChild"), mChild(std::move(aChild)) {} + + NS_IMETHODIMP Run() override { return NS_OK; } + + private: + UniquePtr<GPUChild> mChild; +}; + +/* static */ +void GPUChild::Destroy(UniquePtr<GPUChild>&& aChild) { + NS_DispatchToMainThread(new DeferredDeleteGPUChild(std::move(aChild))); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUChild.h b/gfx/ipc/GPUChild.h new file mode 100644 index 0000000000..e5fb2cca8f --- /dev/null +++ b/gfx/ipc/GPUChild.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_mozilla_gfx_ipc_GPUChild_h_ +#define _include_mozilla_gfx_ipc_GPUChild_h_ + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ipc/CrashReporterHelper.h" +#include "mozilla/gfx/PGPUChild.h" +#include "mozilla/gfx/gfxVarReceiver.h" + +namespace mozilla { + +namespace dom { +class MemoryReportRequestHost; +} // namespace dom +namespace gfx { + +class GPUProcessHost; + +class GPUChild final : public ipc::CrashReporterHelper<GeckoProcessType_GPU>, + public PGPUChild, + public gfxVarReceiver { + typedef mozilla::dom::MemoryReportRequestHost MemoryReportRequestHost; + + public: + explicit GPUChild(GPUProcessHost* aHost); + virtual ~GPUChild(); + + void Init(); + + bool EnsureGPUReady(); + base::ProcessHandle GetChildProcessHandle(); + + // gfxVarReceiver overrides. + void OnVarChanged(const GfxVarUpdate& aVar) override; + + // PGPUChild overrides. + mozilla::ipc::IPCResult RecvInitComplete(const GPUDeviceData& aData); + mozilla::ipc::IPCResult RecvReportCheckerboard(const uint32_t& aSeverity, + const nsCString& aLog); + mozilla::ipc::IPCResult RecvCreateVRProcess(); + mozilla::ipc::IPCResult RecvShutdownVRProcess(); + + mozilla::ipc::IPCResult RecvAccumulateChildHistograms( + nsTArray<HistogramAccumulation>&& aAccumulations); + mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms( + nsTArray<KeyedHistogramAccumulation>&& aAccumulations); + mozilla::ipc::IPCResult RecvUpdateChildScalars( + nsTArray<ScalarAction>&& aScalarActions); + mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars( + nsTArray<KeyedScalarAction>&& aScalarActions); + mozilla::ipc::IPCResult RecvRecordChildEvents( + nsTArray<ChildEventData>&& events); + mozilla::ipc::IPCResult RecvRecordDiscardedData( + const DiscardedData& aDiscardedData); + + void ActorDestroy(ActorDestroyReason aWhy) override; + mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError); + mozilla::ipc::IPCResult RecvNotifyUiObservers(const nsCString& aTopic); + mozilla::ipc::IPCResult RecvNotifyDeviceReset(const GPUDeviceData& aData); + mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport); + mozilla::ipc::IPCResult RecvUpdateFeature(const Feature& aFeature, + const FeatureFailure& aChange); + mozilla::ipc::IPCResult RecvUsedFallback(const Fallback& aFallback, + const nsCString& aMessage); + mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aDetails); + mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported( + const PDMFactory::MediaCodecsSupported& aSupported); + + bool SendRequestMemoryReport(const uint32_t& aGeneration, + const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const Maybe<ipc::FileDescriptor>& aDMDFile); + + static void Destroy(UniquePtr<GPUChild>&& aChild); + + private: + GPUProcessHost* mHost; + UniquePtr<MemoryReportRequestHost> mMemoryReportRequest; + bool mGPUReady; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUChild_h_ diff --git a/gfx/ipc/GPUParent.cpp b/gfx/ipc/GPUParent.cpp new file mode 100644 index 0000000000..86c975bf62 --- /dev/null +++ b/gfx/ipc/GPUParent.cpp @@ -0,0 +1,643 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef XP_WIN +# include "WMF.h" +# include "WMFDecoderModule.h" +#endif +#include "GLContextProvider.h" +#include "GPUParent.h" +#include "GPUProcessHost.h" +#include "GPUProcessManager.h" +#include "GfxInfoBase.h" +#include "ProcessUtils.h" +#include "VRGPUChild.h" +#include "VRManager.h" +#include "VRManagerParent.h" +#include "VsyncBridgeParent.h" +#include "cairo.h" +#include "gfxConfig.h" +#include "gfxCrashReporterUtils.h" +#include "gfxPlatform.h" +#include "mozilla/Assertions.h" +#include "mozilla/HangDetails.h" +#include "mozilla/PerfStats.h" +#include "mozilla/Preferences.h" +#include "mozilla/RemoteDecoderManagerChild.h" +#include "mozilla/RemoteDecoderManagerParent.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/MemoryReportRequest.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/image/ImageMemoryReporter.h" +#include "mozilla/ipc/CrashReporterClient.h" +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/layers/APZInputBridgeParent.h" +#include "mozilla/layers/APZPublicUtils.h" // for apz::InitializeGlobalState +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/MemoryReportingMLGPU.h" +#include "mozilla/layers/UiCompositorControllerParent.h" +#include "mozilla/layers/VideoBridgeParent.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/webrender/WebRenderAPI.h" +#include "nsDebugImpl.h" +#include "nsIGfxInfo.h" +#include "nsThreadManager.h" +#include "nscore.h" +#include "prenv.h" +#include "skia/include/core/SkGraphics.h" +#if defined(XP_WIN) +# include <dwrite.h> +# include <process.h> + +# include "gfxWindowsPlatform.h" +# include "mozilla/WindowsVersion.h" +# include "mozilla/gfx/DeviceManagerDx.h" +# include "mozilla/widget/WinCompositorWindowThread.h" +#else +# include <unistd.h> +#endif +#ifdef MOZ_WIDGET_GTK +# include <gtk/gtk.h> + +# include "skia/include/ports/SkTypeface_cairo.h" +#endif +#ifdef MOZ_GECKO_PROFILER +# include "ChildProfilerController.h" +#endif +#include "nsAppRunner.h" + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) +# include "mozilla/SandboxTestingChild.h" +#endif + +namespace mozilla::gfx { + +using namespace ipc; +using namespace layers; + +static GPUParent* sGPUParent; + +GPUParent::GPUParent() : mLaunchTime(TimeStamp::Now()) { sGPUParent = this; } + +GPUParent::~GPUParent() { sGPUParent = nullptr; } + +/* static */ +GPUParent* GPUParent::GetSingleton() { + MOZ_DIAGNOSTIC_ASSERT(sGPUParent); + return sGPUParent; +} + +bool GPUParent::Init(base::ProcessId aParentPid, const char* aParentBuildID, + MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel) { + // Initialize the thread manager before starting IPC. Otherwise, messages + // may be posted to the main thread and we won't be able to process them. + if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) { + return false; + } + + // Now it's safe to start IPC. + if (NS_WARN_IF(!Open(std::move(aChannel), aParentPid, aIOLoop))) { + return false; + } + + nsDebugImpl::SetMultiprocessMode("GPU"); + + // This must be checked before any IPDL message, which may hit sentinel + // errors due to parent and content processes having different + // versions. + MessageChannel* channel = GetIPCChannel(); + if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) { + // We need to quit this process if the buildID doesn't match the parent's. + // This can occur when an update occurred in the background. + ProcessChild::QuickExit(); + } + + if (NS_FAILED(NS_InitMinimalXPCOM())) { + return false; + } + + // Init crash reporter support. + CrashReporterClient::InitSingleton(this); + + gfxConfig::Init(); + gfxVars::Initialize(); + gfxPlatform::InitNullMetadata(); + // Ensure our Factory is initialised, mainly for gfx logging to work. + gfxPlatform::InitMoz2DLogging(); + mlg::InitializeMemoryReporters(); +#if defined(XP_WIN) + gfxWindowsPlatform::InitMemoryReportersForGPUProcess(); + DeviceManagerDx::Init(); +#endif + + CompositorThreadHolder::Start(); + APZThreadUtils::SetControllerThread(NS_GetCurrentThread()); + apz::InitializeGlobalState(); + LayerTreeOwnerTracker::Initialize(); + CompositorBridgeParent::InitializeStatics(); + mozilla::ipc::SetThisProcessName("GPU Process"); + + return true; +} + +void GPUParent::NotifyDeviceReset() { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction( + "gfx::GPUParent::NotifyDeviceReset", + []() -> void { GPUParent::GetSingleton()->NotifyDeviceReset(); })); + return; + } + + // Reset and reinitialize the compositor devices +#ifdef XP_WIN + if (!DeviceManagerDx::Get()->MaybeResetAndReacquireDevices()) { + // If the device doesn't need to be reset then the device + // has already been reset by a previous NotifyDeviceReset message. + return; + } +#endif + + // Notify the main process that there's been a device reset + // and that they should reset their compositors and repaint + GPUDeviceData data; + RecvGetDeviceStatus(&data); + Unused << SendNotifyDeviceReset(data); +} + +already_AddRefed<PAPZInputBridgeParent> GPUParent::AllocPAPZInputBridgeParent( + const LayersId& aLayersId) { + return MakeAndAddRef<APZInputBridgeParent>(aLayersId); +} + +mozilla::ipc::IPCResult GPUParent::RecvInit( + nsTArray<GfxVarUpdate>&& vars, const DevicePrefs& devicePrefs, + nsTArray<LayerTreeIdMapping>&& aMappings, + nsTArray<GfxInfoFeatureStatus>&& aFeatures) { + for (const auto& var : vars) { + gfxVars::ApplyUpdate(var); + } + + // Inherit device preferences. + gfxConfig::Inherit(Feature::HW_COMPOSITING, devicePrefs.hwCompositing()); + gfxConfig::Inherit(Feature::D3D11_COMPOSITING, + devicePrefs.d3d11Compositing()); + gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, devicePrefs.oglCompositing()); + gfxConfig::Inherit(Feature::ADVANCED_LAYERS, devicePrefs.advancedLayers()); + gfxConfig::Inherit(Feature::DIRECT2D, devicePrefs.useD2D1()); + gfxConfig::Inherit(Feature::WEBGPU, devicePrefs.webGPU()); + gfxConfig::Inherit(Feature::D3D11_HW_ANGLE, devicePrefs.d3d11HwAngle()); + + { // Let the crash reporter know if we've got WR enabled or not. For other + // processes this happens in gfxPlatform::InitWebRenderConfig. + ScopedGfxFeatureReporter reporter("WR", + gfxPlatform::WebRenderPrefEnabled()); + if (gfxVars::UseWebRender()) { + reporter.SetSuccessful(); + } + } + + for (const LayerTreeIdMapping& map : aMappings) { + LayerTreeOwnerTracker::Get()->Map(map.layersId(), map.ownerId()); + } + + widget::GfxInfoBase::SetFeatureStatus(std::move(aFeatures)); + + // We bypass gfxPlatform::Init, so we must initialize any relevant libraries + // here that would normally be initialized there. + SkGraphics::Init(); + +#if defined(XP_WIN) + if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) { + if (DeviceManagerDx::Get()->CreateCompositorDevices() && + gfxVars::RemoteCanvasEnabled()) { + if (DeviceManagerDx::Get()->CreateCanvasDevice()) { + MOZ_ALWAYS_TRUE(Factory::EnsureDWriteFactory()); + } else { + gfxWarning() << "Failed to create canvas device."; + } + } + } + if (gfxVars::UseWebRender()) { + DeviceManagerDx::Get()->CreateDirectCompositionDevice(); + // Ensure to initialize GfxInfo + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + Unused << gfxInfo; + + Factory::EnsureDWriteFactory(); + } +#endif + +#if defined(MOZ_WIDGET_GTK) + char* display_name = PR_GetEnv("MOZ_GDK_DISPLAY"); + if (!display_name) { + bool waylandDisabled = true; +# ifdef MOZ_WAYLAND + waylandDisabled = IsWaylandDisabled(); +# endif + if (waylandDisabled) { + display_name = PR_GetEnv("DISPLAY"); + } + } + if (display_name) { + int argc = 3; + char option_name[] = "--display"; + char* argv[] = {// argv0 is unused because g_set_prgname() was called in + // XRE_InitChildProcess(). + nullptr, option_name, display_name, nullptr}; + char** argvp = argv; + gtk_init(&argc, &argvp); + } else { + gtk_init(nullptr, nullptr); + } + + // Ensure we have an FT library for font instantiation. + // This would normally be set by gfxPlatform::Init(). + // Since we bypass that, we must do it here instead. + if (gfxVars::UseWebRender()) { + FT_Library library = Factory::NewFTLibrary(); + MOZ_ASSERT(library); + Factory::SetFTLibrary(library); + + SkInitCairoFT(true); + } + + // Ensure that GfxInfo::Init is called on the main thread. + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + Unused << gfxInfo; +#endif + + // Make sure to do this *after* we update gfxVars above. + if (gfxVars::UseWebRender()) { + wr::RenderThread::Start(); + image::ImageMemoryReporter::InitForWebRender(); + } +#ifdef XP_WIN + else { + if (gfxVars::UseDoubleBufferingWithCompositor()) { + // This is needed to avoid freezing the window on a device crash on double + // buffering, see bug 1549674. + widget::WinCompositorWindowThread::Start(); + } + } +#endif + + VRManager::ManagerInit(); + // Send a message to the UI process that we're done. + GPUDeviceData data; + RecvGetDeviceStatus(&data); + Unused << SendInitComplete(data); + + // Dispatch a task to run when idle that will determine which codecs are + // usable. The primary goal is to determine if the media feature pack is + // installed. + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThreadQueue( + NS_NewRunnableFunction( + "GPUParent::Supported", + []() { + auto supported = PDMFactory::Supported(); + Unused << GPUParent::GetSingleton()->SendUpdateMediaCodecsSupported( + supported); + }), + 2000 /* 2 seconds timeout */, EventQueuePriority::Idle)); + + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_INITIALIZATION_TIME_MS, + mLaunchTime); + return IPC_OK(); +} + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) +mozilla::ipc::IPCResult GPUParent::RecvInitSandboxTesting( + Endpoint<PSandboxTestingChild>&& aEndpoint) { + if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) { + return IPC_FAIL( + this, "InitSandboxTesting failed to initialise the child process."); + } + return IPC_OK(); +} +#endif + +mozilla::ipc::IPCResult GPUParent::RecvInitCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint) { + CompositorManagerParent::Create(std::move(aEndpoint), /* aIsRoot */ true); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVsyncBridge( + Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) { + mVsyncBridge = VsyncBridgeParent::Start(std::move(aVsyncEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint) { + ImageBridgeParent::CreateForGPUProcess(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVideoBridge( + Endpoint<PVideoBridgeParent>&& aEndpoint) { + VideoBridgeParent::Open(std::move(aEndpoint), VideoBridgeSource::RddProcess); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVRManager( + Endpoint<PVRManagerParent>&& aEndpoint) { + VRManagerParent::CreateForGPUProcess(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitVR( + Endpoint<PVRGPUChild>&& aEndpoint) { + gfx::VRGPUChild::InitForGPUProcess(std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitUiCompositorController( + const LayersId& aRootLayerTreeId, + Endpoint<PUiCompositorControllerParent>&& aEndpoint) { + UiCompositorControllerParent::Start(aRootLayerTreeId, std::move(aEndpoint)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvInitProfiler( + Endpoint<PProfilerChild>&& aEndpoint) { +#ifdef MOZ_GECKO_PROFILER + mProfilerController = ChildProfilerController::Create(std::move(aEndpoint)); +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvUpdateVar(const GfxVarUpdate& aUpdate) { +#if defined(XP_WIN) + auto scopeExit = MakeScopeExit( + [couldUseHWDecoder = gfx::gfxVars::CanUseHardwareVideoDecoding()] { + if (couldUseHWDecoder != gfx::gfxVars::CanUseHardwareVideoDecoding()) { + // The capabilities of the system may have changed, force a refresh by + // re-initializing the WMF PDM. + WMFDecoderModule::Init(); + Unused << GPUParent::GetSingleton()->SendUpdateMediaCodecsSupported( + PDMFactory::Supported(true /* force refresh */)); + } + }); +#endif + gfxVars::ApplyUpdate(aUpdate); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvPreferenceUpdate(const Pref& aPref) { + Preferences::SetPreference(aPref); + return IPC_OK(); +} + +static void CopyFeatureChange(Feature aFeature, Maybe<FeatureFailure>* aOut) { + FeatureState& feature = gfxConfig::GetFeature(aFeature); + if (feature.DisabledByDefault() || feature.IsEnabled()) { + // No change: + // - Disabled-by-default means the parent process told us not to use this + // feature. + // - Enabled means we were told to use this feature, and we didn't + // discover anything + // that would prevent us from doing so. + *aOut = Nothing(); + return; + } + + MOZ_ASSERT(!feature.IsEnabled()); + + nsCString message; + message.AssignASCII(feature.GetFailureMessage()); + + *aOut = + Some(FeatureFailure(feature.GetValue(), message, feature.GetFailureId())); +} + +mozilla::ipc::IPCResult GPUParent::RecvGetDeviceStatus(GPUDeviceData* aOut) { + CopyFeatureChange(Feature::D3D11_COMPOSITING, &aOut->d3d11Compositing()); + CopyFeatureChange(Feature::OPENGL_COMPOSITING, &aOut->oglCompositing()); + CopyFeatureChange(Feature::ADVANCED_LAYERS, &aOut->advancedLayers()); + +#if defined(XP_WIN) + if (DeviceManagerDx* dm = DeviceManagerDx::Get()) { + D3D11DeviceStatus deviceStatus; + dm->ExportDeviceInfo(&deviceStatus); + aOut->gpuDevice() = Some(deviceStatus); + } +#else + aOut->gpuDevice() = Nothing(); +#endif + + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvSimulateDeviceReset( + GPUDeviceData* aOut) { +#if defined(XP_WIN) + DeviceManagerDx::Get()->ForceDeviceReset( + ForcedDeviceResetReason::COMPOSITOR_UPDATED); + DeviceManagerDx::Get()->MaybeResetAndReacquireDevices(); +#endif + if (gfxVars::UseWebRender()) { + wr::RenderThread::Get()->SimulateDeviceReset(); + } + RecvGetDeviceStatus(aOut); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint) { + CompositorManagerParent::Create(std::move(aEndpoint), /* aIsRoot */ false); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint) { + if (!ImageBridgeParent::CreateForContent(std::move(aEndpoint))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentVRManager( + Endpoint<PVRManagerParent>&& aEndpoint) { + if (!VRManagerParent::CreateForContent(std::move(aEndpoint))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNewContentRemoteDecoderManager( + Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) { + if (!RemoteDecoderManagerParent::CreateForContent(std::move(aEndpoint))) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvAddLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping) { + LayerTreeOwnerTracker::Get()->Map(aMapping.layersId(), aMapping.ownerId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvRemoveLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping) { + LayerTreeOwnerTracker::Get()->Unmap(aMapping.layersId(), aMapping.ownerId()); + CompositorBridgeParent::DeallocateLayerTreeId(aMapping.layersId()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvNotifyGpuObservers( + const nsCString& aTopic) { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return IPC_OK(); +} + +/* static */ +void GPUParent::GetGPUProcessName(nsACString& aStr) { + auto processType = XRE_GetProcessType(); + unsigned pid = 0; + if (processType == GeckoProcessType_GPU) { + pid = getpid(); + } else { + MOZ_DIAGNOSTIC_ASSERT(processType == GeckoProcessType_Default); + pid = GPUProcessManager::Get()->GPUProcessPid(); + } + + nsPrintfCString processName("GPU (pid %u)", pid); + aStr.Assign(processName); +} + +mozilla::ipc::IPCResult GPUParent::RecvRequestMemoryReport( + const uint32_t& aGeneration, const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile, + const RequestMemoryReportResolver& aResolver) { + nsAutoCString processName; + GetGPUProcessName(processName); + + mozilla::dom::MemoryReportRequestClient::Start( + aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName, + [&](const MemoryReport& aReport) { + Unused << GetSingleton()->SendAddMemoryReport(aReport); + }, + aResolver); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvShutdownVR() { + if (StaticPrefs::dom_vr_process_enabled_AtStartup()) { + VRGPUChild::Shutdown(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvUpdatePerfStatsCollectionMask( + const uint64_t& aMask) { + PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult GPUParent::RecvCollectPerfStatsJSON( + CollectPerfStatsJSONResolver&& aResolver) { + aResolver(PerfStats::CollectLocalPerfStatsJSON()); + return IPC_OK(); +} + +void GPUParent::ActorDestroy(ActorDestroyReason aWhy) { + if (AbnormalShutdown == aWhy) { + NS_WARNING("Shutting down GPU process early due to a crash!"); + ProcessChild::QuickExit(); + } + +#ifndef NS_FREE_PERMANENT_DATA +# ifdef XP_WIN + wmf::MFShutdown(); +# endif + // No point in going through XPCOM shutdown because we don't keep persistent + // state. + ProcessChild::QuickExit(); +#endif + + // Wait until all RemoteDecoderManagerParent have closed. + mShutdownBlockers.WaitUntilClear(10 * 1000 /* 10s timeout*/) + ->Then(GetCurrentSerialEventTarget(), __func__, [this]() { +#ifdef XP_WIN + wmf::MFShutdown(); +#endif + +#ifdef MOZ_GECKO_PROFILER + if (mProfilerController) { + mProfilerController->Shutdown(); + mProfilerController = nullptr; + } +#endif + + if (mVsyncBridge) { + mVsyncBridge->Shutdown(); + mVsyncBridge = nullptr; + } + RemoteDecoderManagerParent::ShutdownVideoBridge(); + CompositorThreadHolder::Shutdown(); + // There is a case that RenderThread exists when gfxVars::UseWebRender() + // is false. This could happen when WebRender was fallbacked to + // compositor. + if (wr::RenderThread::Get()) { + wr::RenderThread::ShutDown(); + } +#ifdef XP_WIN + if (widget::WinCompositorWindowThread::Get()) { + widget::WinCompositorWindowThread::ShutDown(); + } +#endif + + image::ImageMemoryReporter::ShutdownForWebRender(); + + // Shut down the default GL context provider. + gl::GLContextProvider::Shutdown(); + +#if defined(XP_WIN) + // The above shutdown calls operate on the available context providers + // on most platforms. Windows is a "special snowflake", though, and has + // three context providers available, so we have to shut all of them + // down. We should only support the default GL provider on Windows; + // then, this could go away. Unfortunately, we currently support WGL + // (the default) for WebGL on Optimus. + gl::GLContextProviderEGL::Shutdown(); +#endif + + Factory::ShutDown(); + + // We bypass gfxPlatform shutdown, so we must shutdown any libraries here + // that would normally be handled by it. +#ifdef NS_FREE_PERMANENT_DATA + SkGraphics::PurgeFontCache(); + cairo_debug_reset_static_data(); +#endif + +#if defined(XP_WIN) + DeviceManagerDx::Shutdown(); +#endif + LayerTreeOwnerTracker::Shutdown(); + gfxVars::Shutdown(); + gfxConfig::Shutdown(); + CrashReporterClient::DestroySingleton(); + XRE_ShutdownChildProcess(); + }); +} + +} // namespace mozilla::gfx diff --git a/gfx/ipc/GPUParent.h b/gfx/ipc/GPUParent.h new file mode 100644 index 0000000000..6a6552a342 --- /dev/null +++ b/gfx/ipc/GPUParent.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_gfx_ipc_GPUParent_h__ +#define _include_gfx_ipc_GPUParent_h__ + +#include "mozilla/RefPtr.h" +#include "mozilla/gfx/PGPUParent.h" +#include "mozilla/media/MediaUtils.h" + +namespace mozilla { + +class TimeStamp; +class ChildProfilerController; + +namespace gfx { + +class VsyncBridgeParent; + +class GPUParent final : public PGPUParent { + public: + GPUParent(); + ~GPUParent(); + + static GPUParent* GetSingleton(); + + AsyncBlockers& AsyncShutdownService() { return mShutdownBlockers; } + + // Gets the name of the GPU process, in the format expected by about:memory. + // There must be a GPU process active, and the caller must be either in that + // process or the parent process. + static void GetGPUProcessName(nsACString& aStr); + + bool Init(base::ProcessId aParentPid, const char* aParentBuildID, + MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel); + void NotifyDeviceReset(); + + already_AddRefed<PAPZInputBridgeParent> AllocPAPZInputBridgeParent( + const LayersId& aLayersId); + + mozilla::ipc::IPCResult RecvInit(nsTArray<GfxVarUpdate>&& vars, + const DevicePrefs& devicePrefs, + nsTArray<LayerTreeIdMapping>&& mappings, + nsTArray<GfxInfoFeatureStatus>&& features); + mozilla::ipc::IPCResult RecvInitCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitVsyncBridge( + Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint); + mozilla::ipc::IPCResult RecvInitImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitVideoBridge( + Endpoint<PVideoBridgeParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitVRManager( + Endpoint<PVRManagerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitVR(Endpoint<PVRGPUChild>&& aVRGPUChild); + mozilla::ipc::IPCResult RecvInitUiCompositorController( + const LayersId& aRootLayerTreeId, + Endpoint<PUiCompositorControllerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvInitProfiler( + Endpoint<PProfilerChild>&& aEndpoint); + mozilla::ipc::IPCResult RecvUpdateVar(const GfxVarUpdate& pref); + mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& pref); + mozilla::ipc::IPCResult RecvNewContentCompositorManager( + Endpoint<PCompositorManagerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvNewContentImageBridge( + Endpoint<PImageBridgeParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvNewContentVRManager( + Endpoint<PVRManagerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvNewContentRemoteDecoderManager( + Endpoint<PRemoteDecoderManagerParent>&& aEndpoint); + mozilla::ipc::IPCResult RecvGetDeviceStatus(GPUDeviceData* aOutStatus); + mozilla::ipc::IPCResult RecvSimulateDeviceReset(GPUDeviceData* aOutStatus); + mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping); + mozilla::ipc::IPCResult RecvRemoveLayerTreeIdMapping( + const LayerTreeIdMapping& aMapping); + mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic); + mozilla::ipc::IPCResult RecvRequestMemoryReport( + const uint32_t& generation, const bool& anonymize, + const bool& minimizeMemoryUsage, + const Maybe<ipc::FileDescriptor>& DMDFile, + const RequestMemoryReportResolver& aResolver); + mozilla::ipc::IPCResult RecvShutdownVR(); + + mozilla::ipc::IPCResult RecvUpdatePerfStatsCollectionMask( + const uint64_t& aMask); + mozilla::ipc::IPCResult RecvCollectPerfStatsJSON( + CollectPerfStatsJSONResolver&& aResolver); + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) + mozilla::ipc::IPCResult RecvInitSandboxTesting( + Endpoint<PSandboxTestingChild>&& aEndpoint); +#endif + + void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + const TimeStamp mLaunchTime; + RefPtr<VsyncBridgeParent> mVsyncBridge; +#ifdef MOZ_GECKO_PROFILER + RefPtr<ChildProfilerController> mProfilerController; +#endif + AsyncBlockers mShutdownBlockers; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_GPUParent_h__ diff --git a/gfx/ipc/GPUProcessHost.cpp b/gfx/ipc/GPUProcessHost.cpp new file mode 100644 index 0000000000..47ec5bde8f --- /dev/null +++ b/gfx/ipc/GPUProcessHost.cpp @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GPUProcessHost.h" +#include "chrome/common/process_watcher.h" +#include "gfxPlatform.h" +#include "mozilla/gfx/GPUChild.h" +#include "mozilla/gfx/Logging.h" +#include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_layers.h" +#include "VRGPUChild.h" +#include "ProcessUtils.h" + +namespace mozilla { +namespace gfx { + +using namespace ipc; + +GPUProcessHost::GPUProcessHost(Listener* aListener) + : GeckoChildProcessHost(GeckoProcessType_GPU), + mListener(aListener), + mTaskFactory(this), + mLaunchPhase(LaunchPhase::Unlaunched), + mProcessToken(0), + mShutdownRequested(false), + mChannelClosed(false) { + MOZ_COUNT_CTOR(GPUProcessHost); +} + +GPUProcessHost::~GPUProcessHost() { MOZ_COUNT_DTOR(GPUProcessHost); } + +bool GPUProcessHost::Launch(StringVector aExtraOpts) { + MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched); + MOZ_ASSERT(!mGPUChild); + MOZ_ASSERT(!gfxPlatform::IsHeadless()); + + mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(); + if (!mPrefSerializer->SerializeToSharedMemory()) { + return false; + } + mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts); + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) + mSandboxLevel = Preferences::GetInt("security.sandbox.gpu.level"); +#endif + + mLaunchPhase = LaunchPhase::Waiting; + mLaunchTime = TimeStamp::Now(); + + if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) { + mLaunchPhase = LaunchPhase::Complete; + mPrefSerializer = nullptr; + return false; + } + return true; +} + +bool GPUProcessHost::WaitForLaunch() { + if (mLaunchPhase == LaunchPhase::Complete) { + return !!mGPUChild; + } + + int32_t timeoutMs = + StaticPrefs::layers_gpu_process_startup_timeout_ms_AtStartup(); + + // If one of the following environment variables are set we can effectively + // ignore the timeout - as we can guarantee the compositor process will be + // terminated + if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") || + PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) { + timeoutMs = 0; + } + + // Our caller expects the connection to be finished after we return, so we + // immediately set up the IPDL actor and fire callbacks. The IO thread will + // still dispatch a notification to the main thread - we'll just ignore it. + bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs); + InitAfterConnect(result); + return result; +} + +void GPUProcessHost::OnChannelConnected(int32_t peer_pid) { + MOZ_ASSERT(!NS_IsMainThread()); + + GeckoChildProcessHost::OnChannelConnected(peer_pid); + + // Post a task to the main thread. Take the lock because mTaskFactory is not + // thread-safe. + RefPtr<Runnable> runnable; + { + MonitorAutoLock lock(mMonitor); + runnable = + mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelConnectedTask); + } + NS_DispatchToMainThread(runnable); +} + +void GPUProcessHost::OnChannelError() { + MOZ_ASSERT(!NS_IsMainThread()); + + GeckoChildProcessHost::OnChannelError(); + + // Post a task to the main thread. Take the lock because mTaskFactory is not + // thread-safe. + RefPtr<Runnable> runnable; + { + MonitorAutoLock lock(mMonitor); + runnable = + mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelErrorTask); + } + NS_DispatchToMainThread(runnable); +} + +void GPUProcessHost::OnChannelConnectedTask() { + if (mLaunchPhase == LaunchPhase::Waiting) { + InitAfterConnect(true); + } +} + +void GPUProcessHost::OnChannelErrorTask() { + if (mLaunchPhase == LaunchPhase::Waiting) { + InitAfterConnect(false); + } +} + +static uint64_t sProcessTokenCounter = 0; + +void GPUProcessHost::InitAfterConnect(bool aSucceeded) { + MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting); + MOZ_ASSERT(!mGPUChild); + + mLaunchPhase = LaunchPhase::Complete; + mPrefSerializer = nullptr; + + if (aSucceeded) { + mProcessToken = ++sProcessTokenCounter; + mGPUChild = MakeUnique<GPUChild>(this); + DebugOnly<bool> rv = mGPUChild->Open( + TakeChannel(), base::GetProcId(GetChildProcessHandle())); + MOZ_ASSERT(rv); + + mGPUChild->Init(); + } + + if (mListener) { + mListener->OnProcessLaunchComplete(this); + } +} + +void GPUProcessHost::Shutdown() { + MOZ_ASSERT(!mShutdownRequested); + + mListener = nullptr; + + if (mGPUChild) { + // OnChannelClosed uses this to check if the shutdown was expected or + // unexpected. + mShutdownRequested = true; + + // The channel might already be closed if we got here unexpectedly. + if (!mChannelClosed) { + if (VRGPUChild::IsCreated()) { + VRGPUChild::Get()->Close(); + } + mGPUChild->SendShutdownVR(); + mGPUChild->Close(); + } + +#ifndef NS_FREE_PERMANENT_DATA + // No need to communicate shutdown, the GPU process doesn't need to + // communicate anything back. + KillHard("NormalShutdown"); +#endif + + // If we're shutting down unexpectedly, we're in the middle of handling an + // ActorDestroy for PGPUChild, which is still on the stack. We'll return + // back to OnChannelClosed. + // + // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild + // acknowledges shutdown. + return; + } + + DestroyProcess(); +} + +void GPUProcessHost::OnChannelClosed() { + mChannelClosed = true; + + if (!mShutdownRequested && mListener) { + // This is an unclean shutdown. Notify our listener that we're going away. + mListener->OnProcessUnexpectedShutdown(this); + } else { + DestroyProcess(); + } + + // Release the actor. + GPUChild::Destroy(std::move(mGPUChild)); + MOZ_ASSERT(!mGPUChild); +} + +void GPUProcessHost::KillHard(const char* aReason) { + ProcessHandle handle = GetChildProcessHandle(); + if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) { + NS_WARNING("failed to kill subprocess!"); + } + + SetAlreadyDead(); +} + +uint64_t GPUProcessHost::GetProcessToken() const { return mProcessToken; } + +void GPUProcessHost::KillProcess() { KillHard("DiagnosticKill"); } + +void GPUProcessHost::DestroyProcess() { + // Cancel all tasks. We don't want anything triggering after our caller + // expects this to go away. + { + MonitorAutoLock lock(mMonitor); + mTaskFactory.RevokeAll(); + } + + GetCurrentSerialEventTarget()->Dispatch( + NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); })); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessHost.h b/gfx/ipc/GPUProcessHost.h new file mode 100644 index 0000000000..22f53db067 --- /dev/null +++ b/gfx/ipc/GPUProcessHost.h @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _include_mozilla_gfx_ipc_GPUProcessHost_h_ +#define _include_mozilla_gfx_ipc_GPUProcessHost_h_ + +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/TaskFactory.h" + +namespace mozilla { +namespace ipc { +class SharedPreferenceSerializer; +} +} // namespace mozilla +class nsITimer; + +namespace mozilla { +namespace gfx { + +class GPUChild; + +// GPUProcessHost is the "parent process" container for a subprocess handle and +// IPC connection. It owns the parent process IPDL actor, which in this case, +// is a GPUChild. +// +// GPUProcessHosts are allocated and managed by GPUProcessManager. For all +// intents and purposes it is a singleton, though more than one may be allocated +// at a time due to its shutdown being asynchronous. +class GPUProcessHost final : public mozilla::ipc::GeckoChildProcessHost { + friend class GPUChild; + + public: + class Listener { + public: + virtual void OnProcessLaunchComplete(GPUProcessHost* aHost) {} + + // The GPUProcessHost has unexpectedly shutdown or had its connection + // severed. This is not called if an error occurs after calling + // Shutdown(). + virtual void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) {} + + virtual void OnRemoteProcessDeviceReset(GPUProcessHost* aHost) {} + }; + + explicit GPUProcessHost(Listener* listener); + + // Launch the subprocess asynchronously. On failure, false is returned. + // Otherwise, true is returned, and the OnProcessLaunchComplete listener + // callback will be invoked either when a connection has been established, or + // if a connection could not be established due to an asynchronous error. + // + // @param aExtraOpts (StringVector) + // Extra options to pass to the subprocess. + bool Launch(StringVector aExtraOpts); + + // If the process is being launched, block until it has launched and + // connected. If a launch task is pending, it will fire immediately. + // + // Returns true if the process is successfully connected; false otherwise. + bool WaitForLaunch(); + + // Inform the process that it should clean up its resources and shut down. + // This initiates an asynchronous shutdown sequence. After this method + // returns, it is safe for the caller to forget its pointer to the + // GPUProcessHost. + // + // After this returns, the attached Listener is no longer used. + void Shutdown(); + + // Return the actor for the top-level actor of the process. If the process + // has not connected yet, this returns null. + GPUChild* GetActor() const { return mGPUChild.get(); } + + // Return a unique id for this process, guaranteed not to be shared with any + // past or future instance of GPUProcessHost. + uint64_t GetProcessToken() const; + + bool IsConnected() const { return !!mGPUChild; } + + // Return the time stamp for when we tried to launch the GPU process. This is + // currently used for Telemetry so that we can determine how long GPU + // processes take to spin up. Note this doesn't denote a successful launch, + // just when we attempted launch. + TimeStamp GetLaunchTime() const { return mLaunchTime; } + + // Called on the IO thread. + void OnChannelConnected(int32_t peer_pid) override; + void OnChannelError() override; + + void SetListener(Listener* aListener); + + // Used for tests and diagnostics + void KillProcess(); + + private: + ~GPUProcessHost(); + + // Called on the main thread. + void OnChannelConnectedTask(); + void OnChannelErrorTask(); + + // Called on the main thread after a connection has been established. + void InitAfterConnect(bool aSucceeded); + + // Called on the main thread when the mGPUChild actor is shutting down. + void OnChannelClosed(); + + // Kill the remote process, triggering IPC shutdown. + void KillHard(const char* aReason); + + void DestroyProcess(); + + DISALLOW_COPY_AND_ASSIGN(GPUProcessHost); + + Listener* mListener; + mozilla::ipc::TaskFactory<GPUProcessHost> mTaskFactory; + + enum class LaunchPhase { Unlaunched, Waiting, Complete }; + LaunchPhase mLaunchPhase; + + UniquePtr<GPUChild> mGPUChild; + uint64_t mProcessToken; + + UniquePtr<mozilla::ipc::SharedPreferenceSerializer> mPrefSerializer; + + bool mShutdownRequested; + bool mChannelClosed; + + TimeStamp mLaunchTime; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUProcessHost_h_ diff --git a/gfx/ipc/GPUProcessImpl.cpp b/gfx/ipc/GPUProcessImpl.cpp new file mode 100644 index 0000000000..dbb43243bb --- /dev/null +++ b/gfx/ipc/GPUProcessImpl.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "GPUProcessImpl.h" +#include "mozilla/ipc/IOThreadChild.h" +#include "nsXPCOM.h" +#include "ProcessUtils.h" + +#if defined(OS_WIN) && defined(MOZ_SANDBOX) +# include "mozilla/sandboxTarget.h" +#elif defined(__OpenBSD__) && defined(MOZ_SANDBOX) +# include "mozilla/SandboxSettings.h" +#endif + +namespace mozilla { +namespace gfx { + +using namespace ipc; + +GPUProcessImpl::GPUProcessImpl(ProcessId aParentPid) + : ProcessChild(aParentPid) {} + +GPUProcessImpl::~GPUProcessImpl() = default; + +bool GPUProcessImpl::Init(int aArgc, char* aArgv[]) { +#if defined(MOZ_SANDBOX) && defined(OS_WIN) + mozilla::SandboxTarget::Instance()->StartSandbox(); +#elif defined(__OpenBSD__) && defined(MOZ_SANDBOX) + StartOpenBSDSandbox(GeckoProcessType_GPU); +#endif + char* parentBuildID = nullptr; + char* prefsHandle = nullptr; + char* prefMapHandle = nullptr; + char* prefsLen = nullptr; + char* prefMapSize = nullptr; + for (int i = 1; i < aArgc; i++) { + if (!aArgv[i]) { + continue; + } + if (strcmp(aArgv[i], "-parentBuildID") == 0) { + parentBuildID = aArgv[i + 1]; + +#ifdef XP_WIN + } else if (strcmp(aArgv[i], "-prefsHandle") == 0) { + if (++i == aArgc) { + return false; + } + prefsHandle = aArgv[i]; + } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) { + if (++i == aArgc) { + return false; + } + prefMapHandle = aArgv[i]; +#endif + } else if (strcmp(aArgv[i], "-prefsLen") == 0) { + if (++i == aArgc) { + return false; + } + prefsLen = aArgv[i]; + } else if (strcmp(aArgv[i], "-prefMapSize") == 0) { + if (++i == aArgc) { + return false; + } + prefMapSize = aArgv[i]; + } + } + + SharedPreferenceDeserializer deserializer; + if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle, + prefsLen, prefMapSize)) { + return false; + } + + return mGPU.Init(ParentPid(), parentBuildID, IOThreadChild::message_loop(), + IOThreadChild::TakeChannel()); +} + +void GPUProcessImpl::CleanUp() { NS_ShutdownXPCOM(nullptr); } + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessImpl.h b/gfx/ipc/GPUProcessImpl.h new file mode 100644 index 0000000000..6ea353fe56 --- /dev/null +++ b/gfx/ipc/GPUProcessImpl.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_gfx_ipc_GPUProcessImpl_h__ +#define _include_gfx_ipc_GPUProcessImpl_h__ + +#include "mozilla/ipc/ProcessChild.h" +#include "GPUParent.h" + +#if defined(XP_WIN) +# include "mozilla/mscom/ProcessRuntime.h" +#endif + +namespace mozilla { +namespace gfx { + +// This class owns the subprocess instance of a PGPU - which in this case, +// is a GPUParent. It is instantiated as a singleton in XRE_InitChildProcess. +class GPUProcessImpl final : public ipc::ProcessChild { + public: + explicit GPUProcessImpl(ProcessId aParentPid); + virtual ~GPUProcessImpl(); + + bool Init(int aArgc, char* aArgv[]) override; + void CleanUp() override; + + private: + DISALLOW_COPY_AND_ASSIGN(GPUProcessImpl); + + GPUParent mGPU; + +#if defined(XP_WIN) + // This object initializes and configures COM. + mozilla::mscom::ProcessRuntime mCOMRuntime; +#endif +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_gfx_ipc_GPUProcessImpl_h__ diff --git a/gfx/ipc/GPUProcessListener.h b/gfx/ipc/GPUProcessListener.h new file mode 100644 index 0000000000..a54c890ba0 --- /dev/null +++ b/gfx/ipc/GPUProcessListener.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_mozilla_gfx_ipc_GPUProcessListener_h_ +#define _include_mozilla_gfx_ipc_GPUProcessListener_h_ + +namespace mozilla { +namespace gfx { + +class GPUProcessListener { + public: + virtual ~GPUProcessListener() = default; + + // Called when the compositor has died and the rendering stack must be + // recreated. + virtual void OnCompositorUnexpectedShutdown() {} + + // Called when devices have been reset and tabs must throw away their + // layer managers. + virtual void OnCompositorDeviceReset() {} +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUProcessListener_h_ diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp new file mode 100644 index 0000000000..2ca9576176 --- /dev/null +++ b/gfx/ipc/GPUProcessManager.cpp @@ -0,0 +1,1232 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GPUProcessManager.h" + +#include "gfxConfig.h" +#include "gfxPlatform.h" +#include "GPUProcessHost.h" +#include "GPUProcessListener.h" +#include "mozilla/MemoryReportingProcess.h" +#include "mozilla/Preferences.h" +#include "mozilla/Sprintf.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_media.h" +#include "mozilla/RemoteDecoderManagerChild.h" +#include "mozilla/RemoteDecoderManagerParent.h" +#include "mozilla/Telemetry.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUChild.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/APZInputBridgeChild.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorManagerChild.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/CompositorOptions.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/InProcessCompositorSession.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layers/RemoteCompositorSession.h" +#include "mozilla/widget/PlatformWidgetTypes.h" +#include "nsAppRunner.h" +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING +# include "mozilla/widget/CompositorWidgetChild.h" +#endif +#include "nsBaseWidget.h" +#include "nsContentUtils.h" +#include "VRManagerChild.h" +#include "VRManagerParent.h" +#include "VsyncBridgeChild.h" +#include "VsyncIOThreadHolder.h" +#include "VsyncSource.h" +#include "nsExceptionHandler.h" +#include "nsPrintfCString.h" + +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/widget/AndroidUiThread.h" +# include "mozilla/layers/UiCompositorControllerChild.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +namespace mozilla { +namespace gfx { + +using namespace mozilla::layers; + +enum class FallbackType : uint32_t { + NONE = 0, + DECODINGDISABLED, + DISABLED, +}; + +static StaticAutoPtr<GPUProcessManager> sSingleton; + +GPUProcessManager* GPUProcessManager::Get() { return sSingleton; } + +void GPUProcessManager::Initialize() { + MOZ_ASSERT(XRE_IsParentProcess()); + sSingleton = new GPUProcessManager(); +} + +void GPUProcessManager::Shutdown() { sSingleton = nullptr; } + +GPUProcessManager::GPUProcessManager() + : mTaskFactory(this), + mNextNamespace(0), + mIdNamespace(0), + mResourceId(0), + mNumProcessAttempts(0), + mDeviceResetCount(0), + mProcess(nullptr), + mProcessToken(0), + mGPUChild(nullptr) { + MOZ_COUNT_CTOR(GPUProcessManager); + + mIdNamespace = AllocateNamespace(); + + mDeviceResetLastTime = TimeStamp::Now(); + + LayerTreeOwnerTracker::Initialize(); + CompositorBridgeParent::InitializeStatics(); +} + +GPUProcessManager::~GPUProcessManager() { + MOZ_COUNT_DTOR(GPUProcessManager); + + LayerTreeOwnerTracker::Shutdown(); + + // The GPU process should have already been shut down. + MOZ_ASSERT(!mProcess && !mGPUChild); + + // We should have already removed observers. + MOZ_ASSERT(!mObserver); +} + +NS_IMPL_ISUPPORTS(GPUProcessManager::Observer, nsIObserver); + +GPUProcessManager::Observer::Observer(GPUProcessManager* aManager) + : mManager(aManager) {} + +NS_IMETHODIMP +GPUProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + mManager->OnXPCOMShutdown(); + } else if (!strcmp(aTopic, "nsPref:changed")) { + mManager->OnPreferenceChange(aData); + } + return NS_OK; +} + +void GPUProcessManager::OnXPCOMShutdown() { + if (mObserver) { + nsContentUtils::UnregisterShutdownObserver(mObserver); + Preferences::RemoveObserver(mObserver, ""); + mObserver = nullptr; + } + + CleanShutdown(); +} + +void GPUProcessManager::OnPreferenceChange(const char16_t* aData) { + // A pref changed. If it is useful to do so, inform child processes. + if (!dom::ContentParent::ShouldSyncPreference(aData)) { + return; + } + + // We know prefs are ASCII here. + NS_LossyConvertUTF16toASCII strData(aData); + + mozilla::dom::Pref pref(strData, /* isLocked */ false, Nothing(), Nothing()); + Preferences::GetPreference(&pref); + if (!!mGPUChild) { + MOZ_ASSERT(mQueuedPrefs.IsEmpty()); + mGPUChild->SendPreferenceUpdate(pref); + } else if (IsGPUProcessLaunching()) { + mQueuedPrefs.AppendElement(pref); + } +} + +void GPUProcessManager::LaunchGPUProcess() { + if (mProcess) { + return; + } + + // Start listening for pref changes so we can + // forward them to the process once it is running. + if (!mObserver) { + mObserver = new Observer(this); + nsContentUtils::RegisterShutdownObserver(mObserver); + Preferences::AddStrongObserver(mObserver, ""); + } + + // Start the Vsync I/O thread so can use it as soon as the process launches. + EnsureVsyncIOThread(); + + mNumProcessAttempts++; + + std::vector<std::string> extraArgs; + nsCString parentBuildID(mozilla::PlatformBuildID()); + extraArgs.push_back("-parentBuildID"); + extraArgs.push_back(parentBuildID.get()); + + // The subprocess is launched asynchronously, so we wait for a callback to + // acquire the IPDL actor. + mProcess = new GPUProcessHost(this); + if (!mProcess->Launch(extraArgs)) { + DisableGPUProcess("Failed to launch GPU process"); + } +} + +bool GPUProcessManager::IsGPUProcessLaunching() { + MOZ_ASSERT(NS_IsMainThread()); + return !!mProcess && !mGPUChild; +} + +void GPUProcessManager::DisableGPUProcess(const char* aMessage) { + if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { + return; + } + + gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage); + gfxCriticalNote << aMessage; + + gfxPlatform::NotifyGPUProcessDisabled(); + + Telemetry::Accumulate(Telemetry::GPU_PROCESS_CRASH_FALLBACKS, + uint32_t(FallbackType::DISABLED)); + + DestroyProcess(); + ShutdownVsyncIOThread(); + + // We may have been in the middle of guaranteeing our various services are + // available when one failed. Some callers may fallback to using the same + // process equivalent, and we need to make sure those services are setup + // correctly. We cannot re-enter DisableGPUProcess from this call because we + // know that it is disabled in the config above. + EnsureProtocolsReady(); + + // If we disable the GPU process during reinitialization after a previous + // crash, then we need to tell the content processes again, because they + // need to rebind to the UI process. + HandleProcessLost(); + + // On Windows and Linux, always fallback to software. + // The assumption is that something in the graphics driver is crashing. +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + FallbackToSoftware("GPU Process is disabled, fallback to software solution."); +#endif +} + +bool GPUProcessManager::EnsureGPUReady() { + if (mProcess && !mProcess->IsConnected()) { + if (!mProcess->WaitForLaunch()) { + // If this fails, we should have fired OnProcessLaunchComplete and + // removed the process. + MOZ_ASSERT(!mProcess && !mGPUChild); + return false; + } + } + + if (mGPUChild) { + if (mGPUChild->EnsureGPUReady()) { + return true; + } + + // If the initialization above fails, we likely have a GPU process teardown + // waiting in our message queue (or will soon). We need to ensure we don't + // restart it later because if we fail here, our callers assume they should + // fall back to a combined UI/GPU process. This also ensures our internal + // state is consistent (e.g. process token is reset). + DisableGPUProcess("Failed to initialize GPU process"); + } + + return false; +} + +void GPUProcessManager::EnsureProtocolsReady() { + EnsureCompositorManagerChild(); + EnsureImageBridgeChild(); + EnsureVRManager(); +} + +void GPUProcessManager::EnsureCompositorManagerChild() { + bool gpuReady = EnsureGPUReady(); + if (CompositorManagerChild::IsInitialized(mProcessToken)) { + return; + } + + if (!gpuReady) { + CompositorManagerChild::InitSameProcess(AllocateNamespace(), mProcessToken); + return; + } + + ipc::Endpoint<PCompositorManagerParent> parentPipe; + ipc::Endpoint<PCompositorManagerChild> childPipe; + nsresult rv = PCompositorManager::CreateEndpoints( + mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PCompositorManager endpoints"); + return; + } + + mGPUChild->SendInitCompositorManager(std::move(parentPipe)); + CompositorManagerChild::Init(std::move(childPipe), AllocateNamespace(), + mProcessToken); +} + +void GPUProcessManager::EnsureImageBridgeChild() { + if (ImageBridgeChild::GetSingleton()) { + return; + } + + if (!EnsureGPUReady()) { + ImageBridgeChild::InitSameProcess(AllocateNamespace()); + return; + } + + ipc::Endpoint<PImageBridgeParent> parentPipe; + ipc::Endpoint<PImageBridgeChild> childPipe; + nsresult rv = PImageBridge::CreateEndpoints( + mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PImageBridge endpoints"); + return; + } + + mGPUChild->SendInitImageBridge(std::move(parentPipe)); + ImageBridgeChild::InitWithGPUProcess(std::move(childPipe), + AllocateNamespace()); +} + +void GPUProcessManager::EnsureVRManager() { + if (VRManagerChild::IsCreated()) { + return; + } + + if (!EnsureGPUReady()) { + VRManagerChild::InitSameProcess(); + return; + } + + ipc::Endpoint<PVRManagerParent> parentPipe; + ipc::Endpoint<PVRManagerChild> childPipe; + nsresult rv = PVRManager::CreateEndpoints( + mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PVRManager endpoints"); + return; + } + + mGPUChild->SendInitVRManager(std::move(parentPipe)); + VRManagerChild::InitWithGPUProcess(std::move(childPipe)); +} + +#if defined(MOZ_WIDGET_ANDROID) +already_AddRefed<UiCompositorControllerChild> +GPUProcessManager::CreateUiCompositorController(nsBaseWidget* aWidget, + const LayersId aId) { + RefPtr<UiCompositorControllerChild> result; + + if (!EnsureGPUReady()) { + result = UiCompositorControllerChild::CreateForSameProcess(aId); + } else { + ipc::Endpoint<PUiCompositorControllerParent> parentPipe; + ipc::Endpoint<PUiCompositorControllerChild> childPipe; + nsresult rv = PUiCompositorController::CreateEndpoints( + mGPUChild->OtherPid(), base::GetCurrentProcId(), &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PUiCompositorController endpoints"); + return nullptr; + } + + mGPUChild->SendInitUiCompositorController(aId, std::move(parentPipe)); + result = UiCompositorControllerChild::CreateForGPUProcess( + mProcessToken, std::move(childPipe)); + } + if (result) { + result->SetBaseWidget(aWidget); + } + return result.forget(); +} +#endif // defined(MOZ_WIDGET_ANDROID) + +void GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost) { + MOZ_ASSERT(mProcess && mProcess == aHost); + + if (!mProcess->IsConnected()) { + DisableGPUProcess("Failed to connect GPU process"); + return; + } + + mGPUChild = mProcess->GetActor(); + mProcessToken = mProcess->GetProcessToken(); + + ipc::Endpoint<PVsyncBridgeParent> vsyncParent; + ipc::Endpoint<PVsyncBridgeChild> vsyncChild; + nsresult rv = PVsyncBridge::CreateEndpoints(mGPUChild->OtherPid(), + base::GetCurrentProcId(), + &vsyncParent, &vsyncChild); + if (NS_FAILED(rv)) { + DisableGPUProcess("Failed to create PVsyncBridge endpoints"); + return; + } + + mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken, + std::move(vsyncChild)); + mGPUChild->SendInitVsyncBridge(std::move(vsyncParent)); + + // Flush any pref updates that happened during launch and weren't + // included in the blobs set up in LaunchGPUProcess. + for (const mozilla::dom::Pref& pref : mQueuedPrefs) { + Unused << NS_WARN_IF(!mGPUChild->SendPreferenceUpdate(pref)); + } + mQueuedPrefs.Clear(); + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::GPUProcessStatus, "Running"_ns); + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::GPUProcessLaunchCount, + static_cast<int>(mNumProcessAttempts)); +} + +static bool ShouldLimitDeviceResets(uint32_t count, int32_t deltaMilliseconds) { + // We decide to limit by comparing the amount of resets that have happened + // and time since the last reset to two prefs. + int32_t timeLimit = StaticPrefs::gfx_device_reset_threshold_ms_AtStartup(); + int32_t countLimit = StaticPrefs::gfx_device_reset_limit_AtStartup(); + + bool hasTimeLimit = timeLimit >= 0; + bool hasCountLimit = countLimit >= 0; + + bool triggeredTime = deltaMilliseconds < timeLimit; + bool triggeredCount = count > (uint32_t)countLimit; + + // If we have both prefs set then it needs to trigger both limits, + // otherwise we only test the pref that is set or none + if (hasTimeLimit && hasCountLimit) { + return triggeredTime && triggeredCount; + } else if (hasTimeLimit) { + return triggeredTime; + } else if (hasCountLimit) { + return triggeredCount; + } + + return false; +} + +void GPUProcessManager::ResetCompositors() { + // Note: this will recreate devices in addition to recreating compositors. + // This isn't optimal, but this is only used on linux where acceleration + // isn't enabled by default, and this way we don't need a new code path. + SimulateDeviceReset(); +} + +void GPUProcessManager::SimulateDeviceReset() { + // Make sure we rebuild environment and configuration for accelerated + // features. + gfxPlatform::GetPlatform()->CompositorUpdated(); + + if (mProcess) { + GPUDeviceData data; + if (mGPUChild->SendSimulateDeviceReset(&data)) { + gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); + } + OnRemoteProcessDeviceReset(mProcess); + } else { + OnInProcessDeviceReset(/* aTrackThreshold */ false); + } +} + +bool GPUProcessManager::DisableWebRenderConfig(wr::WebRenderError aError, + const nsCString& aMsg) { + if (!gfx::gfxVars::UseWebRender()) { + return false; + } + // Disable WebRender + if (aError == wr::WebRenderError::INITIALIZE) { + gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable, + "WebRender initialization failed", aMsg); + } else if (aError == wr::WebRenderError::MAKE_CURRENT) { + gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable, + "Failed to make render context current", + "FEATURE_FAILURE_WEBRENDER_MAKE_CURRENT"_ns); + } else if (aError == wr::WebRenderError::RENDER) { + gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable, + "Failed to render WebRender", + "FEATURE_FAILURE_WEBRENDER_RENDER"_ns); + } else if (aError == wr::WebRenderError::NEW_SURFACE) { + gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable, + "Failed to create new surface", + "FEATURE_FAILURE_WEBRENDER_NEW_SURFACE"_ns); + } else if (aError == wr::WebRenderError::EXCESSIVE_RESETS) { + gfxPlatform::DisableWebRender( + gfx::FeatureStatus::Unavailable, "Device resets exceeded threshold", + "FEATURE_FAILURE_WEBRENDER_EXCESSIVE_RESETS"_ns); + } else { + MOZ_ASSERT_UNREACHABLE("Invalid value"); + gfxPlatform::DisableWebRender(gfx::FeatureStatus::Unavailable, + "Unhandled failure reason", + "FEATURE_FAILURE_WEBRENDER_UNHANDLED"_ns); + } + gfx::gfxVars::SetUseWebRenderDCompVideoOverlayWin(false); + +#if defined(MOZ_WIDGET_ANDROID) + // If aError is not wr::WebRenderError::INITIALIZE, nsWindow does not + // re-create LayerManager. Needs to trigger re-creating LayerManager on + // android + if (aError != wr::WebRenderError::INITIALIZE) { + NotifyDisablingWebRender(); + } +#elif defined(MOZ_WIDGET_GTK) + // Hardware compositing should be disabled by default if we aren't using + // WebRender. We had to check if it is enabled at all, because it may + // already have been forced disabled (e.g. safe mode, headless). It may + // still be forced on by the user, and if so, this should have no effect. + gfxConfig::SetFailed(Feature::HW_COMPOSITING, FeatureStatus::Blocked, + "Acceleration blocked by platform", + "FEATURE_FAILURE_LOST_WEBRENDER"_ns); +#endif + + return true; +} + +void GPUProcessManager::DisableWebRender(wr::WebRenderError aError, + const nsCString& aMsg) { + if (DisableWebRenderConfig(aError, aMsg)) { + if (mProcess) { + OnRemoteProcessDeviceReset(mProcess); + } else { + OnInProcessDeviceReset(/* aTrackThreshold */ false); + } + } +} + +void GPUProcessManager::NotifyWebRenderError(wr::WebRenderError aError) { + if (aError == wr::WebRenderError::VIDEO_OVERLAY) { +#ifdef XP_WIN + gfxVars::SetUseWebRenderDCompVideoOverlayWin(false); +#else + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); +#endif + return; + } + DisableWebRender(aError, nsCString()); +} + +/* static */ void GPUProcessManager::RecordDeviceReset( + DeviceResetReason aReason) { + if (aReason != DeviceResetReason::FORCED_RESET) { + Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(aReason)); + } + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::DeviceResetReason, int(aReason)); +} + +bool GPUProcessManager::OnDeviceReset(bool aTrackThreshold) { +#ifdef XP_WIN + // Disable double buffering when device reset happens. + if (!gfxVars::UseWebRender() && gfxVars::UseDoubleBufferingWithCompositor()) { + gfxVars::SetUseDoubleBufferingWithCompositor(false); + } +#endif + + // Ignore resets for thresholding if requested. + if (!aTrackThreshold) { + return false; + } + + // Detect whether the device is resetting too quickly or too much + // indicating that we should give up and use software + mDeviceResetCount++; + + auto newTime = TimeStamp::Now(); + auto delta = (int32_t)(newTime - mDeviceResetLastTime).ToMilliseconds(); + mDeviceResetLastTime = newTime; + + // Returns true if we should disable acceleration due to the reset. + return ShouldLimitDeviceResets(mDeviceResetCount, delta); +} + +void GPUProcessManager::OnInProcessDeviceReset(bool aTrackThreshold) { + if (OnDeviceReset(aTrackThreshold)) { + gfxCriticalNoteOnce << "In-process device reset threshold exceeded"; +#ifdef MOZ_WIDGET_GTK + // FIXME(aosmond): Should we disable WebRender on other platforms? + DisableWebRenderConfig(wr::WebRenderError::EXCESSIVE_RESETS, nsCString()); +#endif + } + + RebuildInProcessSessions(); + NotifyListenersOnCompositeDeviceReset(); +} + +void GPUProcessManager::OnRemoteProcessDeviceReset(GPUProcessHost* aHost) { + if (OnDeviceReset(/* aTrackThreshold */ true)) { + DestroyProcess(); + DisableGPUProcess("GPU processed experienced too many device resets"); + HandleProcessLost(); + return; + } + + RebuildRemoteSessions(); + NotifyListenersOnCompositeDeviceReset(); +} + +void GPUProcessManager::FallbackToSoftware(const char* aMessage) { + gfxConfig::SetFailed(Feature::HW_COMPOSITING, FeatureStatus::Blocked, + aMessage, "GPU_PROCESS_FALLBACK_TO_SOFTWARE"_ns); +#ifdef XP_WIN + gfxConfig::SetFailed(Feature::D3D11_COMPOSITING, FeatureStatus::Blocked, + aMessage, "GPU_PROCESS_FALLBACK_TO_SOFTWARE"_ns); + gfxConfig::SetFailed(Feature::DIRECT2D, FeatureStatus::Blocked, aMessage, + "GPU_PROCESS_FALLBACK_TO_SOFTWARE"_ns); +#endif +} + +void GPUProcessManager::NotifyListenersOnCompositeDeviceReset() { + for (const auto& listener : mListeners) { + listener->OnCompositorDeviceReset(); + } +} + +void GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost) { + MOZ_ASSERT(mProcess && mProcess == aHost); + + if (StaticPrefs::layers_gpu_process_crash_also_crashes_browser()) { + MOZ_CRASH("GPU process crashed and pref is set to crash the browser."); + } + + CompositorManagerChild::OnGPUProcessLost(aHost->GetProcessToken()); + DestroyProcess(); + + if (mNumProcessAttempts > + uint32_t(StaticPrefs::layers_gpu_process_max_restarts())) { + char disableMessage[64]; + SprintfLiteral(disableMessage, "GPU process disabled after %d attempts", + mNumProcessAttempts); + DisableGPUProcess(disableMessage); + } else if (mNumProcessAttempts > + uint32_t(StaticPrefs:: + layers_gpu_process_max_restarts_with_decoder()) && + mDecodeVideoOnGpuProcess) { + mDecodeVideoOnGpuProcess = false; + Telemetry::Accumulate(Telemetry::GPU_PROCESS_CRASH_FALLBACKS, + uint32_t(FallbackType::DECODINGDISABLED)); + HandleProcessLost(); + } else { + Telemetry::Accumulate(Telemetry::GPU_PROCESS_CRASH_FALLBACKS, + uint32_t(FallbackType::NONE)); + HandleProcessLost(); + } +} + +void GPUProcessManager::HandleProcessLost() { + if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { + LaunchGPUProcess(); + } + + // The shutdown and restart sequence for the GPU process is as follows: + // + // (1) The GPU process dies. IPDL will enqueue an ActorDestroy message on + // each channel owning a bridge to the GPU process, on the thread + // owning that channel. + // + // (2) The first channel to process its ActorDestroy message will post a + // message to the main thread to call NotifyRemoteActorDestroyed on + // the GPUProcessManager, which calls OnProcessUnexpectedShutdown if + // it has not handled shutdown for this process yet. + // + // (3) We then notify each widget that its session with the compositor is + // now invalid. The widget is responsible for destroying its layer + // manager and CompositorBridgeChild. Note that at this stage, not + // all actors may have received ActorDestroy yet. CompositorBridgeChild + // may attempt to send messages, and if this happens, it will probably + // report a MsgDropped error. This is okay. + // + // (4) At this point, the UI process has a clean slate: no layers should + // exist for the old compositor. We may make a decision on whether or + // not to re-launch the GPU process. Currently, we do not relaunch it, + // and any new compositors will be created in-process and will default + // to software. + // + // (5) Next we notify each ContentParent of the lost connection. It will + // request new endpoints from the GPUProcessManager and forward them + // to its ContentChild. The parent-side of these endpoints may come + // from the compositor thread of the UI process, or the compositor + // thread of the GPU process. However, no actual compositors should + // exist yet. + // + // (6) Each ContentChild will receive new endpoints. It will destroy its + // Compositor/ImageBridgeChild singletons and recreate them, as well + // as invalidate all retained layers. + // + // (7) In addition, each ContentChild will ask each of its BrowserChildren + // to re-request association with the compositor for the window + // owning the tab. The sequence of calls looks like: + // (a) [CONTENT] ContentChild::RecvReinitRendering + // (b) [CONTENT] BrowserChild::ReinitRendering + // (c) [CONTENT] BrowserChild::SendEnsureLayersConnected + // (d) [UI] BrowserParent::RecvEnsureLayersConnected + // (e) [UI] RenderFrame::EnsureLayersConnected + // (f) [UI] CompositorBridgeChild::SendNotifyChildRecreated + // + // Note that at step (e), RenderFrame will call GetLayerManager + // on the nsIWidget owning the tab. This step ensures that a compositor + // exists for the window. If we decided to launch a new GPU Process, + // at this point we block until the process has launched and we're + // able to create a new window compositor. Otherwise, if compositing + // is now in-process, this will simply create a new + // CompositorBridgeParent in the UI process. If there are multiple tabs + // in the same window, additional tabs will simply return the already- + // established compositor. + // + // Finally, this step serves one other crucial function: tabs must be + // associated with a window compositor or else they can't forward + // layer transactions. So this step both ensures that a compositor + // exists, and that the tab can forward layers. + // + // (8) Last, if the window had no remote tabs, step (7) will not have + // applied, and the window will not have a new compositor just yet. + // The next refresh tick and paint will ensure that one exists, again + // via nsIWidget::GetLayerManager. + RebuildRemoteSessions(); + + // Notify content. This will ensure that each content process re-establishes + // a connection to the compositor thread (whether it's in-process or in a + // newly launched GPU process). + for (const auto& listener : mListeners) { + listener->OnCompositorUnexpectedShutdown(); + } +} + +void GPUProcessManager::RebuildRemoteSessions() { + // Build a list of sessions to notify, since notification might delete + // entries from the list. + nsTArray<RefPtr<RemoteCompositorSession>> sessions; + for (auto& session : mRemoteSessions) { + sessions.AppendElement(session); + } + + // Notify each widget that we have lost the GPU process. This will ensure + // that each widget destroys its layer manager and CompositorBridgeChild. + for (const auto& session : sessions) { + session->NotifySessionLost(); + } +} + +void GPUProcessManager::RebuildInProcessSessions() { + // Build a list of sessions to notify, since notification might delete + // entries from the list. + nsTArray<RefPtr<InProcessCompositorSession>> sessions; + for (auto& session : mInProcessSessions) { + sessions.AppendElement(session); + } + + // Notify each widget that we have lost the GPU process. This will ensure + // that each widget destroys its layer manager and CompositorBridgeChild. + for (const auto& session : sessions) { + session->NotifySessionLost(); + } +} + +void GPUProcessManager::NotifyDisablingWebRender() { +#if defined(MOZ_WIDGET_ANDROID) + for (const auto& session : mRemoteSessions) { + session->NotifyDisablingWebRender(); + } + + for (const auto& session : mInProcessSessions) { + session->NotifyDisablingWebRender(); + } +#endif +} + +void GPUProcessManager::NotifyRemoteActorDestroyed( + const uint64_t& aProcessToken) { + if (!NS_IsMainThread()) { + RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod( + &GPUProcessManager::NotifyRemoteActorDestroyed, aProcessToken); + NS_DispatchToMainThread(task.forget()); + return; + } + + if (mProcessToken != aProcessToken) { + // This token is for an older process; we can safely ignore it. + return; + } + + // One of the bridged top-level actors for the GPU process has been + // prematurely terminated, and we're receiving a notification. This + // can happen if the ActorDestroy for a bridged protocol fires + // before the ActorDestroy for PGPUChild. + OnProcessUnexpectedShutdown(mProcess); +} + +void GPUProcessManager::CleanShutdown() { + DestroyProcess(); + mVsyncIOThread = nullptr; +} + +void GPUProcessManager::KillProcess() { + if (!mProcess) { + return; + } + + mProcess->KillProcess(); +} + +void GPUProcessManager::DestroyProcess() { + if (!mProcess) { + return; + } + + mProcess->Shutdown(); + mProcessToken = 0; + mProcess = nullptr; + mGPUChild = nullptr; + mQueuedPrefs.Clear(); + if (mVsyncBridge) { + mVsyncBridge->Close(); + mVsyncBridge = nullptr; + } + + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::GPUProcessStatus, "Destroyed"_ns); +} + +already_AddRefed<CompositorSession> GPUProcessManager::CreateTopLevelCompositor( + nsBaseWidget* aWidget, LayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, + bool* aRetryOut) { + MOZ_ASSERT(aRetryOut); + + LayersId layerTreeId = AllocateLayerTreeId(); + + EnsureProtocolsReady(); + + RefPtr<CompositorSession> session; + + if (EnsureGPUReady()) { + session = + CreateRemoteSession(aWidget, aLayerManager, layerTreeId, aScale, + aOptions, aUseExternalSurfaceSize, aSurfaceSize); + if (!session) { + // We couldn't create a remote compositor, so abort the process. + DisableGPUProcess("Failed to create remote compositor"); + *aRetryOut = true; + return nullptr; + } + } else { + session = InProcessCompositorSession::Create( + aWidget, aLayerManager, layerTreeId, aScale, aOptions, + aUseExternalSurfaceSize, aSurfaceSize, AllocateNamespace()); + } + +#if defined(MOZ_WIDGET_ANDROID) + if (session) { + // Nothing to do if controller gets a nullptr + RefPtr<UiCompositorControllerChild> controller = + CreateUiCompositorController(aWidget, session->RootLayerTreeId()); + session->SetUiCompositorControllerChild(controller); + } +#endif // defined(MOZ_WIDGET_ANDROID) + + *aRetryOut = false; + return session.forget(); +} + +RefPtr<CompositorSession> GPUProcessManager::CreateRemoteSession( + nsBaseWidget* aWidget, LayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize) { +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING + widget::CompositorWidgetInitData initData; + aWidget->GetCompositorWidgetInitData(&initData); + + RefPtr<CompositorBridgeChild> child = + CompositorManagerChild::CreateWidgetCompositorBridge( + mProcessToken, aLayerManager, AllocateNamespace(), aScale, aOptions, + aUseExternalSurfaceSize, aSurfaceSize); + if (!child) { + gfxCriticalNote << "Failed to create CompositorBridgeChild"; + return nullptr; + } + + RefPtr<CompositorVsyncDispatcher> dispatcher = + aWidget->GetCompositorVsyncDispatcher(); + RefPtr<widget::CompositorWidgetVsyncObserver> observer = + new widget::CompositorWidgetVsyncObserver(mVsyncBridge, aRootLayerTreeId); + + widget::CompositorWidgetChild* widget = + new widget::CompositorWidgetChild(dispatcher, observer, initData); + if (!child->SendPCompositorWidgetConstructor(widget, initData)) { + return nullptr; + } + if (!widget->Initialize()) { + return nullptr; + } + if (!child->SendInitialize(aRootLayerTreeId)) { + return nullptr; + } + + RefPtr<APZCTreeManagerChild> apz = nullptr; + if (aOptions.UseAPZ()) { + PAPZCTreeManagerChild* papz = + child->SendPAPZCTreeManagerConstructor(LayersId{0}); + if (!papz) { + return nullptr; + } + apz = static_cast<APZCTreeManagerChild*>(papz); + + RefPtr<APZInputBridgeChild> pinput = new APZInputBridgeChild(); + if (!mGPUChild->SendPAPZInputBridgeConstructor(pinput, aRootLayerTreeId)) { + return nullptr; + } + apz->SetInputBridge(pinput); + } + + return new RemoteCompositorSession(aWidget, child, widget, apz, + aRootLayerTreeId); +#else + gfxCriticalNote << "Platform does not support out-of-process compositing"; + return nullptr; +#endif +} + +bool GPUProcessManager::CreateContentBridges( + base::ProcessId aOtherProcess, + ipc::Endpoint<PCompositorManagerChild>* aOutCompositor, + ipc::Endpoint<PImageBridgeChild>* aOutImageBridge, + ipc::Endpoint<PVRManagerChild>* aOutVRBridge, + ipc::Endpoint<PRemoteDecoderManagerChild>* aOutVideoManager, + nsTArray<uint32_t>* aNamespaces) { + if (!CreateContentCompositorManager(aOtherProcess, aOutCompositor) || + !CreateContentImageBridge(aOtherProcess, aOutImageBridge) || + !CreateContentVRManager(aOtherProcess, aOutVRBridge)) { + return false; + } + // VideoDeocderManager is only supported in the GPU process, so we allow this + // to be fallible. + CreateContentRemoteDecoderManager(aOtherProcess, aOutVideoManager); + // Allocates 3 namespaces(for CompositorManagerChild, CompositorBridgeChild + // and ImageBridgeChild) + aNamespaces->AppendElement(AllocateNamespace()); + aNamespaces->AppendElement(AllocateNamespace()); + aNamespaces->AppendElement(AllocateNamespace()); + return true; +} + +bool GPUProcessManager::CreateContentCompositorManager( + base::ProcessId aOtherProcess, + ipc::Endpoint<PCompositorManagerChild>* aOutEndpoint) { + ipc::Endpoint<PCompositorManagerParent> parentPipe; + ipc::Endpoint<PCompositorManagerChild> childPipe; + + base::ProcessId parentPid = + EnsureGPUReady() ? mGPUChild->OtherPid() : base::GetCurrentProcId(); + + nsresult rv = PCompositorManager::CreateEndpoints(parentPid, aOtherProcess, + &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor manager: " + << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentCompositorManager(std::move(parentPipe)); + } else if (!CompositorManagerParent::Create(std::move(parentPipe), + /* aIsRoot */ false)) { + return false; + } + + *aOutEndpoint = std::move(childPipe); + return true; +} + +bool GPUProcessManager::CreateContentImageBridge( + base::ProcessId aOtherProcess, + ipc::Endpoint<PImageBridgeChild>* aOutEndpoint) { + EnsureImageBridgeChild(); + + base::ProcessId parentPid = + EnsureGPUReady() ? mGPUChild->OtherPid() : base::GetCurrentProcId(); + + ipc::Endpoint<PImageBridgeParent> parentPipe; + ipc::Endpoint<PImageBridgeChild> childPipe; + nsresult rv = PImageBridge::CreateEndpoints(parentPid, aOtherProcess, + &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor bridge: " + << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentImageBridge(std::move(parentPipe)); + } else { + if (!ImageBridgeParent::CreateForContent(std::move(parentPipe))) { + return false; + } + } + + *aOutEndpoint = std::move(childPipe); + return true; +} + +base::ProcessId GPUProcessManager::GPUProcessPid() { + base::ProcessId gpuPid = mGPUChild ? mGPUChild->OtherPid() : -1; + return gpuPid; +} + +bool GPUProcessManager::CreateContentVRManager( + base::ProcessId aOtherProcess, + ipc::Endpoint<PVRManagerChild>* aOutEndpoint) { + EnsureVRManager(); + + base::ProcessId parentPid = + EnsureGPUReady() ? mGPUChild->OtherPid() : base::GetCurrentProcId(); + + ipc::Endpoint<PVRManagerParent> parentPipe; + ipc::Endpoint<PVRManagerChild> childPipe; + nsresult rv = PVRManager::CreateEndpoints(parentPid, aOtherProcess, + &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor bridge: " + << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentVRManager(std::move(parentPipe)); + } else { + if (!VRManagerParent::CreateForContent(std::move(parentPipe))) { + return false; + } + } + + *aOutEndpoint = std::move(childPipe); + return true; +} + +void GPUProcessManager::CreateContentRemoteDecoderManager( + base::ProcessId aOtherProcess, + ipc::Endpoint<PRemoteDecoderManagerChild>* aOutEndpoint) { + if (!EnsureGPUReady() || !StaticPrefs::media_gpu_process_decoder() || + !mDecodeVideoOnGpuProcess) { + return; + } + + ipc::Endpoint<PRemoteDecoderManagerParent> parentPipe; + ipc::Endpoint<PRemoteDecoderManagerChild> childPipe; + + nsresult rv = PRemoteDecoderManager::CreateEndpoints( + mGPUChild->OtherPid(), aOtherProcess, &parentPipe, &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content video decoder: " + << hexa(int(rv)); + return; + } + + mGPUChild->SendNewContentRemoteDecoderManager(std::move(parentPipe)); + + *aOutEndpoint = std::move(childPipe); +} + +void GPUProcessManager::InitVideoBridge( + ipc::Endpoint<PVideoBridgeParent>&& aVideoBridge) { + if (EnsureGPUReady()) { + mGPUChild->SendInitVideoBridge(std::move(aVideoBridge)); + } +} + +void GPUProcessManager::MapLayerTreeId(LayersId aLayersId, + base::ProcessId aOwningId) { + LayerTreeOwnerTracker::Get()->Map(aLayersId, aOwningId); + + if (EnsureGPUReady()) { + mGPUChild->SendAddLayerTreeIdMapping( + LayerTreeIdMapping(aLayersId, aOwningId)); + } +} + +void GPUProcessManager::UnmapLayerTreeId(LayersId aLayersId, + base::ProcessId aOwningId) { + LayerTreeOwnerTracker::Get()->Unmap(aLayersId, aOwningId); + + if (EnsureGPUReady()) { + mGPUChild->SendRemoveLayerTreeIdMapping( + LayerTreeIdMapping(aLayersId, aOwningId)); + return; + } + CompositorBridgeParent::DeallocateLayerTreeId(aLayersId); +} + +bool GPUProcessManager::IsLayerTreeIdMapped(LayersId aLayersId, + base::ProcessId aRequestingId) { + return LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, aRequestingId); +} + +LayersId GPUProcessManager::AllocateLayerTreeId() { + // Allocate tree id by using id namespace. + // By it, tree id does not conflict with external image id and + // async image pipeline id. + MOZ_ASSERT(NS_IsMainThread()); + ++mResourceId; + if (mResourceId == UINT32_MAX) { + // Move to next id namespace. + mIdNamespace = AllocateNamespace(); + mResourceId = 1; + } + + uint64_t layerTreeId = mIdNamespace; + layerTreeId = (layerTreeId << 32) | mResourceId; + return LayersId{layerTreeId}; +} + +uint32_t GPUProcessManager::AllocateNamespace() { + MOZ_ASSERT(NS_IsMainThread()); + return ++mNextNamespace; +} + +bool GPUProcessManager::AllocateAndConnectLayerTreeId( + PCompositorBridgeChild* aCompositorBridge, base::ProcessId aOtherPid, + LayersId* aOutLayersId, CompositorOptions* aOutCompositorOptions) { + LayersId layersId = AllocateLayerTreeId(); + *aOutLayersId = layersId; + + if (!mGPUChild || !aCompositorBridge) { + // If we're not remoting to another process, or there is no compositor, + // then we'll send at most one message. In this case we can just keep + // the old behavior of making sure the mapping occurs, and maybe sending + // a creation notification. + MapLayerTreeId(layersId, aOtherPid); + if (!aCompositorBridge) { + return false; + } + return aCompositorBridge->SendNotifyChildCreated(layersId, + aOutCompositorOptions); + } + + // Use the combined message path. + LayerTreeOwnerTracker::Get()->Map(layersId, aOtherPid); + return aCompositorBridge->SendMapAndNotifyChildCreated(layersId, aOtherPid, + aOutCompositorOptions); +} + +void GPUProcessManager::EnsureVsyncIOThread() { + if (mVsyncIOThread) { + return; + } + + mVsyncIOThread = new VsyncIOThreadHolder(); + MOZ_RELEASE_ASSERT(mVsyncIOThread->Start()); +} + +void GPUProcessManager::ShutdownVsyncIOThread() { mVsyncIOThread = nullptr; } + +void GPUProcessManager::RegisterRemoteProcessSession( + RemoteCompositorSession* aSession) { + mRemoteSessions.AppendElement(aSession); +} + +void GPUProcessManager::UnregisterRemoteProcessSession( + RemoteCompositorSession* aSession) { + mRemoteSessions.RemoveElement(aSession); +} + +void GPUProcessManager::RegisterInProcessSession( + InProcessCompositorSession* aSession) { + mInProcessSessions.AppendElement(aSession); +} + +void GPUProcessManager::UnregisterInProcessSession( + InProcessCompositorSession* aSession) { + mInProcessSessions.RemoveElement(aSession); +} + +void GPUProcessManager::AddListener(GPUProcessListener* aListener) { + mListeners.AppendElement(aListener); +} + +void GPUProcessManager::RemoveListener(GPUProcessListener* aListener) { + mListeners.RemoveElement(aListener); +} + +bool GPUProcessManager::NotifyGpuObservers(const char* aTopic) { + if (!EnsureGPUReady()) { + return false; + } + nsCString topic(aTopic); + mGPUChild->SendNotifyGpuObservers(topic); + return true; +} + +class GPUMemoryReporter : public MemoryReportingProcess { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GPUMemoryReporter, override) + + bool IsAlive() const override { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + return !!gpm->GetGPUChild(); + } + return false; + } + + bool SendRequestMemoryReport( + const uint32_t& aGeneration, const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const Maybe<ipc::FileDescriptor>& aDMDFile) override { + GPUChild* child = GetChild(); + if (!child) { + return false; + } + + return child->SendRequestMemoryReport(aGeneration, aAnonymize, + aMinimizeMemoryUsage, aDMDFile); + } + + int32_t Pid() const override { + if (GPUChild* child = GetChild()) { + return (int32_t)child->OtherPid(); + } + return 0; + } + + private: + GPUChild* GetChild() const { + if (GPUProcessManager* gpm = GPUProcessManager::Get()) { + if (GPUChild* child = gpm->GetGPUChild()) { + return child; + } + } + return nullptr; + } + + protected: + ~GPUMemoryReporter() = default; +}; + +RefPtr<MemoryReportingProcess> GPUProcessManager::GetProcessMemoryReporter() { + if (!EnsureGPUReady()) { + return nullptr; + } + return new GPUMemoryReporter(); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessManager.h b/gfx/ipc/GPUProcessManager.h new file mode 100644 index 0000000000..5d65c011cb --- /dev/null +++ b/gfx/ipc/GPUProcessManager.h @@ -0,0 +1,314 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_mozilla_gfx_ipc_GPUProcessManager_h_ +#define _include_mozilla_gfx_ipc_GPUProcessManager_h_ + +#include "base/basictypes.h" +#include "base/process.h" +#include "Units.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/gfx/GPUProcessHost.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/TaskFactory.h" +#include "mozilla/ipc/Transport.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/webrender/WebRenderTypes.h" +#include "nsIObserver.h" +#include "nsThreadUtils.h" +class nsBaseWidget; +enum class DeviceResetReason; + +namespace mozilla { +class MemoryReportingProcess; +class PRemoteDecoderManagerChild; +namespace layers { +class IAPZCTreeManager; +class CompositorOptions; +class CompositorSession; +class CompositorUpdateObserver; +class PCompositorBridgeChild; +class PCompositorManagerChild; +class PImageBridgeChild; +class PVideoBridgeParent; +class RemoteCompositorSession; +class InProcessCompositorSession; +class UiCompositorControllerChild; +class LayerManager; +} // namespace layers +namespace widget { +class CompositorWidget; +} // namespace widget +namespace dom { +class ContentParent; +class BrowserParent; +} // namespace dom +namespace ipc { +class GeckoChildProcessHost; +} // namespace ipc +namespace gfx { + +class GPUChild; +class GPUProcessListener; +class PVRManagerChild; +class VsyncBridgeChild; +class VsyncIOThreadHolder; + +// The GPUProcessManager is a singleton responsible for creating GPU-bound +// objects that may live in another process. Currently, it provides access +// to the compositor via CompositorBridgeParent. +class GPUProcessManager final : public GPUProcessHost::Listener { + friend class layers::RemoteCompositorSession; + friend class layers::InProcessCompositorSession; + + typedef layers::CompositorOptions CompositorOptions; + typedef layers::CompositorSession CompositorSession; + typedef layers::CompositorUpdateObserver CompositorUpdateObserver; + typedef layers::IAPZCTreeManager IAPZCTreeManager; + typedef layers::LayerManager LayerManager; + typedef layers::LayersId LayersId; + typedef layers::PCompositorBridgeChild PCompositorBridgeChild; + typedef layers::PCompositorManagerChild PCompositorManagerChild; + typedef layers::PImageBridgeChild PImageBridgeChild; + typedef layers::PVideoBridgeParent PVideoBridgeParent; + typedef layers::RemoteCompositorSession RemoteCompositorSession; + typedef layers::InProcessCompositorSession InProcessCompositorSession; + typedef layers::UiCompositorControllerChild UiCompositorControllerChild; + + public: + static void Initialize(); + static void Shutdown(); + static GPUProcessManager* Get(); + + ~GPUProcessManager(); + + // If not using a GPU process, launch a new GPU process asynchronously. + void LaunchGPUProcess(); + bool IsGPUProcessLaunching(); + + // Ensure that GPU-bound methods can be used. If no GPU process is being + // used, or one is launched and ready, this function returns immediately. + // Otherwise it blocks until the GPU process has finished launching. + bool EnsureGPUReady(); + + already_AddRefed<CompositorSession> CreateTopLevelCompositor( + nsBaseWidget* aWidget, LayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, + bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, + bool* aRetry); + + bool CreateContentBridges( + base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PCompositorManagerChild>* aOutCompositor, + mozilla::ipc::Endpoint<PImageBridgeChild>* aOutImageBridge, + mozilla::ipc::Endpoint<PVRManagerChild>* aOutVRBridge, + mozilla::ipc::Endpoint<PRemoteDecoderManagerChild>* aOutVideoManager, + nsTArray<uint32_t>* aNamespaces); + + // Initialize GPU process with consuming end of PVideoBridge. + void InitVideoBridge( + mozilla::ipc::Endpoint<PVideoBridgeParent>&& aVideoBridge); + + // Maps the layer tree and process together so that aOwningPID is allowed + // to access aLayersId across process. + void MapLayerTreeId(LayersId aLayersId, base::ProcessId aOwningId); + + // Release compositor-thread resources referred to by |aID|. + // + // Must run on the content main thread. + void UnmapLayerTreeId(LayersId aLayersId, base::ProcessId aOwningId); + + // Checks to see if aLayersId and aRequestingPID have been mapped by + // MapLayerTreeId + bool IsLayerTreeIdMapped(LayersId aLayersId, base::ProcessId aRequestingId); + + // Allocate an ID that can be used to refer to a layer tree and + // associated resources that live only on the compositor thread. + // + // Must run on the browser main thread. + LayersId AllocateLayerTreeId(); + + // Allocate an ID that can be used as Namespace and + // Must run on the browser main thread. + uint32_t AllocateNamespace(); + + // Allocate a layers ID and connect it to a compositor. If the compositor is + // null, the connect operation will not be performed, but an ID will still be + // allocated. This must be called from the browser main thread. + // + // Note that a layer tree id is always allocated, even if this returns false. + bool AllocateAndConnectLayerTreeId(PCompositorBridgeChild* aCompositorBridge, + base::ProcessId aOtherPid, + LayersId* aOutLayersId, + CompositorOptions* aOutCompositorOptions); + + // Destroy and recreate all of the compositors + void ResetCompositors(); + + // Record the device reset in telemetry / annotate the crash report. + static void RecordDeviceReset(DeviceResetReason aReason); + + void OnProcessLaunchComplete(GPUProcessHost* aHost) override; + void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override; + void SimulateDeviceReset(); + void DisableWebRender(wr::WebRenderError aError, const nsCString& aMsg); + void NotifyWebRenderError(wr::WebRenderError aError); + void OnInProcessDeviceReset(bool aTrackThreshold); + void OnRemoteProcessDeviceReset(GPUProcessHost* aHost) override; + void NotifyListenersOnCompositeDeviceReset(); + + // Notify the GPUProcessManager that a top-level PGPU protocol has been + // terminated. This may be called from any thread. + void NotifyRemoteActorDestroyed(const uint64_t& aProcessToken); + + void AddListener(GPUProcessListener* aListener); + void RemoveListener(GPUProcessListener* aListener); + + // Send a message to the GPU process observer service to broadcast. Returns + // true if the message was sent, false if not. + bool NotifyGpuObservers(const char* aTopic); + + // Used for tests and diagnostics + void KillProcess(); + + // Returns -1 if there is no GPU process, or the platform pid for it. + base::ProcessId GPUProcessPid(); + + // If a GPU process is present, create a MemoryReportingProcess object. + // Otherwise, return null. + RefPtr<MemoryReportingProcess> GetProcessMemoryReporter(); + + // Returns access to the PGPU protocol if a GPU process is present. + GPUChild* GetGPUChild() { return mGPUChild; } + + // Returns whether or not a GPU process was ever launched. + bool AttemptedGPUProcess() const { return mNumProcessAttempts > 0; } + + // Returns the process host + GPUProcessHost* Process() { return mProcess; } + + private: + // Called from our xpcom-shutdown observer. + void OnXPCOMShutdown(); + void OnPreferenceChange(const char16_t* aData); + + bool CreateContentCompositorManager( + base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PCompositorManagerChild>* aOutEndpoint); + bool CreateContentImageBridge( + base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PImageBridgeChild>* aOutEndpoint); + bool CreateContentVRManager( + base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PVRManagerChild>* aOutEndpoint); + void CreateContentRemoteDecoderManager( + base::ProcessId aOtherProcess, + mozilla::ipc::Endpoint<PRemoteDecoderManagerChild>* aOutEndPoint); + + // Called from RemoteCompositorSession. We track remote sessions so we can + // notify their owning widgets that the session must be restarted. + void RegisterRemoteProcessSession(RemoteCompositorSession* aSession); + void UnregisterRemoteProcessSession(RemoteCompositorSession* aSession); + + // Called from InProcessCompositorSession. We track in process sessino so we + // can notify their owning widgets that the session must be restarted + void RegisterInProcessSession(InProcessCompositorSession* aSession); + void UnregisterInProcessSession(InProcessCompositorSession* aSession); + + void RebuildRemoteSessions(); + void RebuildInProcessSessions(); + + // Returns true if we crossed the threshold such that we should disable + // acceleration. + bool OnDeviceReset(bool aTrackThreshold); + + // Returns true if WebRender was enabled and is now disabled. + bool DisableWebRenderConfig(wr::WebRenderError aError, const nsCString& aMsg); + + void NotifyDisablingWebRender(); + + void FallbackToSoftware(const char* aMessage); + + private: + GPUProcessManager(); + + // Permanently disable the GPU process and record a message why. + void DisableGPUProcess(const char* aMessage); + + // Shutdown the GPU process. + void CleanShutdown(); + void DestroyProcess(); + + void HandleProcessLost(); + + void EnsureVsyncIOThread(); + void ShutdownVsyncIOThread(); + + void EnsureProtocolsReady(); + void EnsureCompositorManagerChild(); + void EnsureImageBridgeChild(); + void EnsureVRManager(); + +#if defined(MOZ_WIDGET_ANDROID) + already_AddRefed<UiCompositorControllerChild> CreateUiCompositorController( + nsBaseWidget* aWidget, const LayersId aId); +#endif // defined(MOZ_WIDGET_ANDROID) + + RefPtr<CompositorSession> CreateRemoteSession( + nsBaseWidget* aWidget, LayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize); + + DISALLOW_COPY_AND_ASSIGN(GPUProcessManager); + + class Observer final : public nsIObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + explicit Observer(GPUProcessManager* aManager); + + protected: + virtual ~Observer() = default; + + GPUProcessManager* mManager; + }; + friend class Observer; + + private: + bool mDecodeVideoOnGpuProcess = true; + + RefPtr<Observer> mObserver; + mozilla::ipc::TaskFactory<GPUProcessManager> mTaskFactory; + RefPtr<VsyncIOThreadHolder> mVsyncIOThread; + uint32_t mNextNamespace; + uint32_t mIdNamespace; + uint32_t mResourceId; + uint32_t mNumProcessAttempts; + + nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions; + nsTArray<RefPtr<InProcessCompositorSession>> mInProcessSessions; + nsTArray<GPUProcessListener*> mListeners; + + uint32_t mDeviceResetCount; + TimeStamp mDeviceResetLastTime; + + // Fields that are associated with the current GPU process. + GPUProcessHost* mProcess; + uint64_t mProcessToken; + GPUChild* mGPUChild; + RefPtr<VsyncBridgeChild> mVsyncBridge; + // Collects any pref changes that occur during process launch (after + // the initial map is passed in command-line arguments) to be sent + // when the process can receive IPC messages. + nsTArray<mozilla::dom::Pref> mQueuedPrefs; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_GPUProcessManager_h_ diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h new file mode 100644 index 0000000000..09acb9c856 --- /dev/null +++ b/gfx/ipc/GfxMessageUtils.h @@ -0,0 +1,1244 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __GFXMESSAGEUTILS_H__ +#define __GFXMESSAGEUTILS_H__ + +#include "FilterSupport.h" +#include "ImageTypes.h" +#include "RegionBuilder.h" +#include "chrome/common/ipc_message_utils.h" +#include "gfxFeature.h" +#include "gfxFallback.h" +#include "gfxPoint.h" +#include "gfxRect.h" +#include "gfxTelemetry.h" +#include "gfxTypes.h" +#include "ipc/EnumSerializer.h" +#include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/gfx/CrossProcessPaint.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/gfx/ScaleFactor.h" +#include "mozilla/gfx/ScaleFactors2D.h" +#include "nsRect.h" +#include "nsRegion.h" +#include "mozilla/Array.h" +#include "mozilla/ipc/IPDLParamTraits.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/ShmemMessageUtils.h" + +#include <stdint.h> + +#ifdef _MSC_VER +# pragma warning(disable : 4800) +#endif + +namespace mozilla { + +typedef gfxImageFormat PixelFormat; + +} // namespace mozilla + +namespace IPC { + +template <> +struct ParamTraits<mozilla::gfx::Matrix> { + typedef mozilla::gfx::Matrix paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam._11); + WriteParam(aMsg, aParam._12); + WriteParam(aMsg, aParam._21); + WriteParam(aMsg, aParam._22); + WriteParam(aMsg, aParam._31); + WriteParam(aMsg, aParam._32); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (ReadParam(aMsg, aIter, &aResult->_11) && + ReadParam(aMsg, aIter, &aResult->_12) && + ReadParam(aMsg, aIter, &aResult->_21) && + ReadParam(aMsg, aIter, &aResult->_22) && + ReadParam(aMsg, aIter, &aResult->_31) && + ReadParam(aMsg, aIter, &aResult->_32)) + return true; + + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) { + aLog->append(StringPrintf(L"[[%g %g] [%g %g] [%g %g]]", aParam._11, + aParam._12, aParam._21, aParam._22, aParam._31, + aParam._32)); + } +}; + +template <> +struct ParamTraits<mozilla::gfx::Matrix4x4> { + typedef mozilla::gfx::Matrix4x4 paramType; + + static void Write(Message* msg, const paramType& param) { +#define Wr(_f) WriteParam(msg, param._f) + Wr(_11); + Wr(_12); + Wr(_13); + Wr(_14); + Wr(_21); + Wr(_22); + Wr(_23); + Wr(_24); + Wr(_31); + Wr(_32); + Wr(_33); + Wr(_34); + Wr(_41); + Wr(_42); + Wr(_43); + Wr(_44); +#undef Wr + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { +#define Rd(_f) ReadParam(msg, iter, &result->_f) + return (Rd(_11) && Rd(_12) && Rd(_13) && Rd(_14) && Rd(_21) && Rd(_22) && + Rd(_23) && Rd(_24) && Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) && + Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44)); +#undef Rd + } +}; + +template <> +struct ParamTraits<mozilla::gfx::Matrix5x4> { + typedef mozilla::gfx::Matrix5x4 paramType; + + static void Write(Message* msg, const paramType& param) { +#define Wr(_f) WriteParam(msg, param._f) + Wr(_11); + Wr(_12); + Wr(_13); + Wr(_14); + Wr(_21); + Wr(_22); + Wr(_23); + Wr(_24); + Wr(_31); + Wr(_32); + Wr(_33); + Wr(_34); + Wr(_41); + Wr(_42); + Wr(_43); + Wr(_44); + Wr(_51); + Wr(_52); + Wr(_53); + Wr(_54); +#undef Wr + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { +#define Rd(_f) ReadParam(msg, iter, &result->_f) + return (Rd(_11) && Rd(_12) && Rd(_13) && Rd(_14) && Rd(_21) && Rd(_22) && + Rd(_23) && Rd(_24) && Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) && + Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44) && Rd(_51) && Rd(_52) && + Rd(_53) && Rd(_54)); +#undef Rd + } +}; + +template <> +struct ParamTraits<gfxPoint> { + typedef gfxPoint paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.x); + WriteParam(aMsg, aParam.y); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->x) && + ReadParam(aMsg, aIter, &aResult->y)); + } +}; + +template <> +struct ParamTraits<gfxSize> { + typedef gfxSize paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.width); + WriteParam(aMsg, aParam.height); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (ReadParam(aMsg, aIter, &aResult->width) && + ReadParam(aMsg, aIter, &aResult->height)) + return true; + + return false; + } +}; + +template <> +struct ParamTraits<gfxRect> { + typedef gfxRect paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.X()); + WriteParam(aMsg, aParam.Y()); + WriteParam(aMsg, aParam.Width()); + WriteParam(aMsg, aParam.Height()); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + auto x = aResult->X(); + auto y = aResult->Y(); + auto w = aResult->Width(); + auto h = aResult->Height(); + + bool retVal = (ReadParam(aMsg, aIter, &x) && ReadParam(aMsg, aIter, &y) && + ReadParam(aMsg, aIter, &w) && ReadParam(aMsg, aIter, &h)); + aResult->SetRect(x, y, w, h); + return retVal; + } +}; + +template <> +struct ParamTraits<gfxContentType> + : public ContiguousEnumSerializer<gfxContentType, gfxContentType::COLOR, + gfxContentType::SENTINEL> {}; + +template <> +struct ParamTraits<gfxSurfaceType> + : public ContiguousEnumSerializer<gfxSurfaceType, gfxSurfaceType::Image, + gfxSurfaceType::Max> {}; + +template <> +struct ParamTraits<mozilla::gfx::SamplingFilter> + : public ContiguousEnumSerializer<mozilla::gfx::SamplingFilter, + mozilla::gfx::SamplingFilter::GOOD, + mozilla::gfx::SamplingFilter::SENTINEL> { +}; + +template <> +struct ParamTraits<mozilla::gfx::BackendType> + : public ContiguousEnumSerializer<mozilla::gfx::BackendType, + mozilla::gfx::BackendType::NONE, + mozilla::gfx::BackendType::BACKEND_LAST> { +}; + +template <> +struct ParamTraits<mozilla::gfx::Feature> + : public ContiguousEnumSerializer<mozilla::gfx::Feature, + mozilla::gfx::Feature::HW_COMPOSITING, + mozilla::gfx::Feature::NumValues> {}; + +template <> +struct ParamTraits<mozilla::gfx::Fallback> + : public ContiguousEnumSerializer< + mozilla::gfx::Fallback, + mozilla::gfx::Fallback::NO_CONSTANT_BUFFER_OFFSETTING, + mozilla::gfx::Fallback::NumValues> {}; + +template <> +struct ParamTraits<mozilla::gfx::FeatureStatus> + : public ContiguousEnumSerializer<mozilla::gfx::FeatureStatus, + mozilla::gfx::FeatureStatus::Unused, + mozilla::gfx::FeatureStatus::LAST> {}; + +template <> +struct ParamTraits<mozilla::gfx::LightType> + : public ContiguousEnumSerializer<mozilla::gfx::LightType, + mozilla::gfx::LightType::None, + mozilla::gfx::LightType::Max> {}; + +template <> +struct ParamTraits<mozilla::gfx::ColorSpace> + : public ContiguousEnumSerializer<mozilla::gfx::ColorSpace, + mozilla::gfx::ColorSpace::SRGB, + mozilla::gfx::ColorSpace::Max> {}; + +template <> +struct ParamTraits<mozilla::gfx::CompositionOp> + : public ContiguousEnumSerializerInclusive< + mozilla::gfx::CompositionOp, mozilla::gfx::CompositionOp::OP_OVER, + mozilla::gfx::CompositionOp::OP_COUNT> {}; + +/* +template <> +struct ParamTraits<mozilla::PixelFormat> + : public EnumSerializer<mozilla::PixelFormat, + SurfaceFormat::A8R8G8B8_UINT32, + SurfaceFormat::UNKNOWN> +{}; +*/ + +template <> +struct ParamTraits<mozilla::gfx::sRGBColor> { + typedef mozilla::gfx::sRGBColor paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.r); + WriteParam(msg, param.g); + WriteParam(msg, param.b); + WriteParam(msg, param.a); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return ( + ReadParam(msg, iter, &result->r) && ReadParam(msg, iter, &result->g) && + ReadParam(msg, iter, &result->b) && ReadParam(msg, iter, &result->a)); + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DeviceColor> { + typedef mozilla::gfx::DeviceColor paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.r); + WriteParam(msg, param.g); + WriteParam(msg, param.b); + WriteParam(msg, param.a); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return ( + ReadParam(msg, iter, &result->r) && ReadParam(msg, iter, &result->g) && + ReadParam(msg, iter, &result->b) && ReadParam(msg, iter, &result->a)); + } +}; + +template <> +struct ParamTraits<nsPoint> { + typedef nsPoint paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.x); + WriteParam(msg, param.y); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y)); + } +}; + +template <> +struct ParamTraits<nsIntPoint> { + typedef nsIntPoint paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.x); + WriteParam(msg, param.y); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y)); + } +}; + +template <typename T> +struct ParamTraits<mozilla::gfx::IntSizeTyped<T>> { + typedef mozilla::gfx::IntSizeTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.width); + WriteParam(msg, param.height); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->width) && + ReadParam(msg, iter, &result->height)); + } +}; + +template <typename Region, typename Rect, typename Iter> +struct RegionParamTraits { + typedef Region paramType; + + static void Write(Message* msg, const paramType& param) { + for (auto iter = param.RectIter(); !iter.Done(); iter.Next()) { + const Rect& r = iter.Get(); + MOZ_RELEASE_ASSERT(!r.IsEmpty(), "GFX: rect is empty."); + WriteParam(msg, r); + } + // empty rects are sentinel values because nsRegions will never + // contain them + WriteParam(msg, Rect()); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + RegionBuilder<Region> builder; + Rect rect; + while (ReadParam(msg, iter, &rect)) { + if (rect.IsEmpty()) { + *result = builder.ToRegion(); + return true; + } + builder.OrWith(rect); + } + + return false; + } +}; + +template <class Units> +struct ParamTraits<mozilla::gfx::IntRegionTyped<Units>> + : RegionParamTraits< + mozilla::gfx::IntRegionTyped<Units>, + mozilla::gfx::IntRectTyped<Units>, + typename mozilla::gfx::IntRegionTyped<Units>::RectIterator> {}; + +template <> +struct ParamTraits<mozilla::gfx::IntSize> { + typedef mozilla::gfx::IntSize paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.width); + WriteParam(msg, param.height); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->width) && + ReadParam(msg, iter, &result->height)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::CoordTyped<T>> { + typedef mozilla::gfx::CoordTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.value); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->value)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntCoordTyped<T>> { + typedef mozilla::gfx::IntCoordTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.value); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->value)); + } +}; + +template <class T, class U> +struct ParamTraits<mozilla::gfx::ScaleFactor<T, U>> { + typedef mozilla::gfx::ScaleFactor<T, U> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.scale); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->scale)); + } +}; + +template <class T, class U> +struct ParamTraits<mozilla::gfx::ScaleFactors2D<T, U>> { + typedef mozilla::gfx::ScaleFactors2D<T, U> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.xScale); + WriteParam(msg, param.yScale); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->xScale) && + ReadParam(msg, iter, &result->yScale)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::PointTyped<T>> { + typedef mozilla::gfx::PointTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.x); + WriteParam(msg, param.y); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y)); + } +}; + +template <class F, class T> +struct ParamTraits<mozilla::gfx::Point3DTyped<F, T>> { + typedef mozilla::gfx::Point3DTyped<F, T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.x); + WriteParam(msg, param.y); + WriteParam(msg, param.z); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y) && + ReadParam(msg, iter, &result->z)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntPointTyped<T>> { + typedef mozilla::gfx::IntPointTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.x); + WriteParam(msg, param.y); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::SizeTyped<T>> { + typedef mozilla::gfx::SizeTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.width); + WriteParam(msg, param.height); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->width) && + ReadParam(msg, iter, &result->height)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::RectTyped<T>> { + typedef mozilla::gfx::RectTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.X()); + WriteParam(msg, param.Y()); + WriteParam(msg, param.Width()); + WriteParam(msg, param.Height()); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + auto x = result->X(); + auto y = result->Y(); + auto w = result->Width(); + auto h = result->Height(); + + bool retVal = (ReadParam(msg, iter, &x) && ReadParam(msg, iter, &y) && + ReadParam(msg, iter, &w) && ReadParam(msg, iter, &h)); + result->SetRect(x, y, w, h); + return retVal; + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::RectAbsoluteTyped<T>> { + typedef mozilla::gfx::RectAbsoluteTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.Left()); + WriteParam(msg, param.Top()); + WriteParam(msg, param.Right()); + WriteParam(msg, param.Bottom()); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + auto l = result->Left(); + auto t = result->Top(); + auto r = result->Right(); + auto b = result->Bottom(); + + bool retVal = (ReadParam(msg, iter, &l) && ReadParam(msg, iter, &t) && + ReadParam(msg, iter, &r) && ReadParam(msg, iter, &b)); + result->SetBox(l, t, r, b); + return retVal; + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntRectTyped<T>> { + typedef mozilla::gfx::IntRectTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.X()); + WriteParam(msg, param.Y()); + WriteParam(msg, param.Width()); + WriteParam(msg, param.Height()); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + auto x = result->X(); + auto y = result->Y(); + auto w = result->Width(); + auto h = result->Height(); + + bool retVal = (ReadParam(msg, iter, &x) && ReadParam(msg, iter, &y) && + ReadParam(msg, iter, &w) && ReadParam(msg, iter, &h)); + result->SetRect(x, y, w, h); + return retVal; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::Margin> { + typedef mozilla::gfx::Margin paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.top); + WriteParam(msg, param.right); + WriteParam(msg, param.bottom); + WriteParam(msg, param.left); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->top) && + ReadParam(msg, iter, &result->right) && + ReadParam(msg, iter, &result->bottom) && + ReadParam(msg, iter, &result->left)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::MarginTyped<T>> { + typedef mozilla::gfx::MarginTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.top); + WriteParam(msg, param.right); + WriteParam(msg, param.bottom); + WriteParam(msg, param.left); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->top) && + ReadParam(msg, iter, &result->right) && + ReadParam(msg, iter, &result->bottom) && + ReadParam(msg, iter, &result->left)); + } +}; + +template <class T> +struct ParamTraits<mozilla::gfx::IntMarginTyped<T>> { + typedef mozilla::gfx::IntMarginTyped<T> paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.top); + WriteParam(msg, param.right); + WriteParam(msg, param.bottom); + WriteParam(msg, param.left); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + return (ReadParam(msg, iter, &result->top) && + ReadParam(msg, iter, &result->right) && + ReadParam(msg, iter, &result->bottom) && + ReadParam(msg, iter, &result->left)); + } +}; + +template <> +struct ParamTraits<nsRect> { + typedef nsRect paramType; + + static void Write(Message* msg, const paramType& param) { + WriteParam(msg, param.X()); + WriteParam(msg, param.Y()); + WriteParam(msg, param.Width()); + WriteParam(msg, param.Height()); + } + + static bool Read(const Message* msg, PickleIterator* iter, + paramType* result) { + auto x = result->X(); + auto y = result->Y(); + auto w = result->Width(); + auto h = result->Height(); + bool retVal = (ReadParam(msg, iter, &x) && ReadParam(msg, iter, &y) && + ReadParam(msg, iter, &w) && ReadParam(msg, iter, &h)); + result->SetRect(x, y, w, h); + return retVal; + } +}; + +template <> +struct ParamTraits<nsRegion> + : RegionParamTraits<nsRegion, nsRect, nsRegion::RectIterator> {}; + +template <> +struct ParamTraits<GeckoProcessType> + : public ContiguousEnumSerializer< + GeckoProcessType, GeckoProcessType_Default, GeckoProcessType_End> {}; + +template <> +struct ParamTraits<mozilla::gfx::SurfaceFormat> + : public ContiguousEnumSerializer<mozilla::gfx::SurfaceFormat, + mozilla::gfx::SurfaceFormat::B8G8R8A8, + mozilla::gfx::SurfaceFormat::UNKNOWN> {}; + +template <> +struct ParamTraits<mozilla::gfx::ColorDepth> + : public ContiguousEnumSerializer<mozilla::gfx::ColorDepth, + mozilla::gfx::ColorDepth::COLOR_8, + mozilla::gfx::ColorDepth::UNKNOWN> {}; + +template <> +struct ParamTraits<mozilla::gfx::ColorRange> + : public ContiguousEnumSerializer<mozilla::gfx::ColorRange, + mozilla::gfx::ColorRange::LIMITED, + mozilla::gfx::ColorRange::UNKNOWN> {}; + +template <> +struct ParamTraits<mozilla::gfx::YUVColorSpace> + : public ContiguousEnumSerializer< + mozilla::gfx::YUVColorSpace, mozilla::gfx::YUVColorSpace::BT601, + mozilla::gfx::YUVColorSpace::_NUM_COLORSPACE> {}; + +template <> +struct ParamTraits<mozilla::StereoMode> + : public ContiguousEnumSerializer<mozilla::StereoMode, + mozilla::StereoMode::MONO, + mozilla::StereoMode::MAX> {}; + +template <> +struct ParamTraits<mozilla::gfx::ImplicitlyCopyableFloatArray> + : public ParamTraits<nsTArray<float>> { + typedef mozilla::gfx::ImplicitlyCopyableFloatArray paramType; +}; + +template <> +struct ParamTraits<mozilla::gfx::EmptyAttributes> { + typedef mozilla::gfx::EmptyAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) {} + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::MergeAttributes> { + typedef mozilla::gfx::MergeAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) {} + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ToAlphaAttributes> { + typedef mozilla::gfx::ToAlphaAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) {} + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::TileAttributes> { + typedef mozilla::gfx::TileAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) {} + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::BlendAttributes> { + typedef mozilla::gfx::BlendAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mBlendMode); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return ReadParam(aMsg, aIter, &aResult->mBlendMode); + } +}; + +template <> +struct ParamTraits<mozilla::gfx::MorphologyAttributes> { + typedef mozilla::gfx::MorphologyAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mOperator); + WriteParam(aMsg, aParam.mRadii); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mOperator) || + !ReadParam(aMsg, aIter, &aResult->mRadii)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::FloodAttributes> { + typedef mozilla::gfx::FloodAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mColor); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mColor)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::OpacityAttributes> { + typedef mozilla::gfx::OpacityAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mOpacity); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mOpacity)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::OffsetAttributes> { + typedef mozilla::gfx::OffsetAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mValue); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mValue)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DisplacementMapAttributes> { + typedef mozilla::gfx::DisplacementMapAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mScale); + WriteParam(aMsg, aParam.mXChannel); + WriteParam(aMsg, aParam.mYChannel); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mScale) || + !ReadParam(aMsg, aIter, &aResult->mXChannel) || + !ReadParam(aMsg, aIter, &aResult->mYChannel)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::TurbulenceAttributes> { + typedef mozilla::gfx::TurbulenceAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mOffset); + WriteParam(aMsg, aParam.mBaseFrequency); + WriteParam(aMsg, aParam.mSeed); + WriteParam(aMsg, aParam.mOctaves); + WriteParam(aMsg, aParam.mStitchable); + WriteParam(aMsg, aParam.mType); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mOffset) || + !ReadParam(aMsg, aIter, &aResult->mBaseFrequency) || + !ReadParam(aMsg, aIter, &aResult->mSeed) || + !ReadParam(aMsg, aIter, &aResult->mOctaves) || + !ReadParam(aMsg, aIter, &aResult->mStitchable) || + !ReadParam(aMsg, aIter, &aResult->mType)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ImageAttributes> { + typedef mozilla::gfx::ImageAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mFilter); + WriteParam(aMsg, aParam.mInputIndex); + WriteParam(aMsg, aParam.mTransform); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mFilter) || + !ReadParam(aMsg, aIter, &aResult->mInputIndex) || + !ReadParam(aMsg, aIter, &aResult->mTransform)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::GaussianBlurAttributes> { + typedef mozilla::gfx::GaussianBlurAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mStdDeviation); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mStdDeviation)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DropShadowAttributes> { + typedef mozilla::gfx::DropShadowAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mStdDeviation); + WriteParam(aMsg, aParam.mOffset); + WriteParam(aMsg, aParam.mColor); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mStdDeviation) || + !ReadParam(aMsg, aIter, &aResult->mOffset) || + !ReadParam(aMsg, aIter, &aResult->mColor)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ColorMatrixAttributes> { + typedef mozilla::gfx::ColorMatrixAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mType); + WriteParam(aMsg, aParam.mValues); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mType) || + !ReadParam(aMsg, aIter, &aResult->mValues)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ComponentTransferAttributes> { + typedef mozilla::gfx::ComponentTransferAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + for (int i = 0; i < 4; ++i) { + WriteParam(aMsg, aParam.mTypes[i]); + } + for (int i = 0; i < 4; ++i) { + WriteParam(aMsg, aParam.mValues[i]); + } + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + for (int i = 0; i < 4; ++i) { + if (!ReadParam(aMsg, aIter, &aResult->mTypes[i])) { + return false; + } + } + for (int i = 0; i < 4; ++i) { + if (!ReadParam(aMsg, aIter, &aResult->mValues[i])) { + return false; + } + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::ConvolveMatrixAttributes> { + typedef mozilla::gfx::ConvolveMatrixAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mKernelSize); + WriteParam(aMsg, aParam.mKernelMatrix); + WriteParam(aMsg, aParam.mDivisor); + WriteParam(aMsg, aParam.mBias); + WriteParam(aMsg, aParam.mTarget); + WriteParam(aMsg, aParam.mEdgeMode); + WriteParam(aMsg, aParam.mKernelUnitLength); + WriteParam(aMsg, aParam.mPreserveAlpha); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mKernelSize) || + !ReadParam(aMsg, aIter, &aResult->mKernelMatrix) || + !ReadParam(aMsg, aIter, &aResult->mDivisor) || + !ReadParam(aMsg, aIter, &aResult->mBias) || + !ReadParam(aMsg, aIter, &aResult->mTarget) || + !ReadParam(aMsg, aIter, &aResult->mEdgeMode) || + !ReadParam(aMsg, aIter, &aResult->mKernelUnitLength) || + !ReadParam(aMsg, aIter, &aResult->mPreserveAlpha)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::DiffuseLightingAttributes> { + typedef mozilla::gfx::DiffuseLightingAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mLightType); + WriteParam(aMsg, aParam.mLightValues); + WriteParam(aMsg, aParam.mSurfaceScale); + WriteParam(aMsg, aParam.mKernelUnitLength); + WriteParam(aMsg, aParam.mColor); + WriteParam(aMsg, aParam.mLightingConstant); + WriteParam(aMsg, aParam.mSpecularExponent); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mLightType) || + !ReadParam(aMsg, aIter, &aResult->mLightValues) || + !ReadParam(aMsg, aIter, &aResult->mSurfaceScale) || + !ReadParam(aMsg, aIter, &aResult->mKernelUnitLength) || + !ReadParam(aMsg, aIter, &aResult->mColor) || + !ReadParam(aMsg, aIter, &aResult->mLightingConstant) || + !ReadParam(aMsg, aIter, &aResult->mSpecularExponent)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::SpecularLightingAttributes> { + typedef mozilla::gfx::SpecularLightingAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mLightType); + WriteParam(aMsg, aParam.mLightValues); + WriteParam(aMsg, aParam.mSurfaceScale); + WriteParam(aMsg, aParam.mKernelUnitLength); + WriteParam(aMsg, aParam.mColor); + WriteParam(aMsg, aParam.mLightingConstant); + WriteParam(aMsg, aParam.mSpecularExponent); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mLightType) || + !ReadParam(aMsg, aIter, &aResult->mLightValues) || + !ReadParam(aMsg, aIter, &aResult->mSurfaceScale) || + !ReadParam(aMsg, aIter, &aResult->mKernelUnitLength) || + !ReadParam(aMsg, aIter, &aResult->mColor) || + !ReadParam(aMsg, aIter, &aResult->mLightingConstant) || + !ReadParam(aMsg, aIter, &aResult->mSpecularExponent)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::CompositeAttributes> { + typedef mozilla::gfx::CompositeAttributes paramType; + + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mOperator); + WriteParam(aMsg, aParam.mCoefficients); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mOperator) || + !ReadParam(aMsg, aIter, &aResult->mCoefficients)) { + return false; + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::Glyph> { + typedef mozilla::gfx::Glyph paramType; + static void Write(Message* aMsg, const paramType& aParam) { + WriteParam(aMsg, aParam.mIndex); + WriteParam(aMsg, aParam.mPosition); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + return (ReadParam(aMsg, aIter, &aResult->mIndex) && + ReadParam(aMsg, aIter, &aResult->mPosition)); + } +}; + +template <typename T, size_t Length> +struct ParamTraits<mozilla::Array<T, Length>> { + typedef mozilla::Array<T, Length> paramType; + static void Write(Message* aMsg, const paramType& aParam) { + for (size_t i = 0; i < Length; i++) { + WriteParam(aMsg, aParam[i]); + } + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, + paramType* aResult) { + for (size_t i = 0; i < Length; i++) { + if (!ReadParam<T>(aMsg, aIter, &aResult->operator[](i))) { + return false; + } + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::SideBits> + : public BitFlagsEnumSerializer<mozilla::SideBits, + mozilla::SideBits::eAll> {}; + +} /* namespace IPC */ + +namespace mozilla { +namespace ipc { + +template <> +struct IPDLParamTraits<gfx::PaintFragment> { + typedef mozilla::gfx::PaintFragment paramType; + static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType&& aParam) { + Shmem shmem; + if (aParam.mSize.IsEmpty() || + !aActor->AllocShmem(aParam.mRecording.mLen, SharedMemory::TYPE_BASIC, + &shmem)) { + WriteParam(aMsg, gfx::IntSize(0, 0)); + return; + } + + memcpy(shmem.get<uint8_t>(), aParam.mRecording.mData, + aParam.mRecording.mLen); + + WriteParam(aMsg, aParam.mSize); + WriteIPDLParam(aMsg, aActor, std::move(shmem)); + WriteParam(aMsg, aParam.mDependencies); + } + + static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, + IProtocol* aActor, paramType* aResult) { + if (!ReadParam(aMsg, aIter, &aResult->mSize)) { + return false; + } + if (aResult->mSize.IsEmpty()) { + return true; + } + Shmem shmem; + if (!ReadIPDLParam(aMsg, aIter, aActor, &shmem) || + !ReadParam(aMsg, aIter, &aResult->mDependencies)) { + aActor->DeallocShmem(shmem); + return false; + } + + if (!aResult->mRecording.Allocate(shmem.Size<uint8_t>())) { + aResult->mSize.SizeTo(0, 0); + aActor->DeallocShmem(shmem); + return true; + } + + memcpy(aResult->mRecording.mData, shmem.get<uint8_t>(), + shmem.Size<uint8_t>()); + aActor->DeallocShmem(shmem); + return true; + } +}; + +} // namespace ipc +} // namespace mozilla + +#endif /* __GFXMESSAGEUTILS_H__ */ diff --git a/gfx/ipc/GraphicsMessages.ipdlh b/gfx/ipc/GraphicsMessages.ipdlh new file mode 100644 index 0000000000..f0f74e3102 --- /dev/null +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h"; +using struct mozilla::null_t from "mozilla/ipc/IPCCore.h"; +using mozilla::gfx::FeatureStatus from "gfxTelemetry.h"; +using mozilla::gfx::BackendType from "mozilla/gfx/Types.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; +using gfxImageFormat from "mozilla/gfx/Types.h"; +using mozilla::gfx::D3D11Checks::VideoFormatOption from "mozilla/gfx/D3D11Checks.h"; +using mozilla::gfx::D3D11Checks::VideoFormatOptionSet from "mozilla/gfx/D3D11Checks.h"; + +namespace mozilla { +namespace gfx { + +struct D3D11DeviceStatus +{ + bool isWARP; + bool textureSharingWorks; + uint32_t featureLevel; + DxgiAdapterDesc adapter; + int32_t sequenceNumber; + VideoFormatOptionSet formatOptions; +}; + +struct DevicePrefs +{ + FeatureStatus hwCompositing; + FeatureStatus d3d11Compositing; + FeatureStatus oglCompositing; + FeatureStatus advancedLayers; + FeatureStatus useD2D1; + FeatureStatus webGPU; + FeatureStatus d3d11HwAngle; +}; + +struct ContentDeviceData +{ + DevicePrefs prefs; + D3D11DeviceStatus d3d11; + uint8_t[] cmsOutputProfileData; +}; + +// Represents the state of a feature that has failed to initialize. +struct FeatureFailure +{ + FeatureStatus status; + nsCString message; + nsCString failureId; +}; + +struct GPUDeviceData +{ + // If a feature state has changed from Enabled -> Failure, these will be non- + // null. + FeatureFailure? d3d11Compositing; + FeatureFailure? oglCompositing; + FeatureFailure? advancedLayers; + D3D11DeviceStatus? gpuDevice; + FeatureFailure? webGPU; +}; + +union GfxVarValue +{ + BackendType; + bool; + gfxImageFormat; + IntSize; + nsCString; + nsString; + int32_t; +}; + +struct GfxVarUpdate +{ + uint32_t index; + GfxVarValue value; +}; + +struct GfxInfoFeatureStatus +{ + int32_t feature; + int32_t status; + nsCString failureId; +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/InProcessCompositorSession.cpp b/gfx/ipc/InProcessCompositorSession.cpp new file mode 100644 index 0000000000..0a87ac9459 --- /dev/null +++ b/gfx/ipc/InProcessCompositorSession.cpp @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InProcessCompositorSession.h" + +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorManagerChild.h" +#include "mozilla/layers/CompositorManagerParent.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/widget/CompositorWidget.h" +#include "mozilla/widget/PlatformWidgetTypes.h" +#include "nsBaseWidget.h" + +namespace mozilla { +namespace layers { + +InProcessCompositorSession::InProcessCompositorSession( + nsBaseWidget* aWidget, widget::CompositorWidget* aCompositorWidget, + CompositorBridgeChild* aChild, CompositorBridgeParent* aParent) + : CompositorSession(aWidget, aCompositorWidget->AsDelegate(), aChild, + aParent->RootLayerTreeId()), + mCompositorBridgeParent(aParent), + mCompositorWidget(aCompositorWidget) { + gfx::GPUProcessManager::Get()->RegisterInProcessSession(this); +} + +/* static */ +RefPtr<InProcessCompositorSession> InProcessCompositorSession::Create( + nsBaseWidget* aWidget, LayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint32_t aNamespace) { + widget::CompositorWidgetInitData initData; + aWidget->GetCompositorWidgetInitData(&initData); + + RefPtr<CompositorWidget> widget = + CompositorWidget::CreateLocal(initData, aOptions, aWidget); + RefPtr<CompositorBridgeParent> parent = + CompositorManagerParent::CreateSameProcessWidgetCompositorBridge( + aScale, aOptions, aUseExternalSurfaceSize, aSurfaceSize); + MOZ_ASSERT(parent); + parent->InitSameProcess(widget, aRootLayerTreeId); + + RefPtr<CompositorBridgeChild> child = + CompositorManagerChild::CreateSameProcessWidgetCompositorBridge( + aLayerManager, aNamespace); + MOZ_ASSERT(child); + + return new InProcessCompositorSession(aWidget, widget, child, parent); +} + +void InProcessCompositorSession::NotifySessionLost() { + mWidget->NotifyCompositorSessionLost(this); +} + +CompositorBridgeParent* InProcessCompositorSession::GetInProcessBridge() const { + return mCompositorBridgeParent; +} + +void InProcessCompositorSession::SetContentController( + GeckoContentController* aController) { + mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, + aController); +} + +RefPtr<IAPZCTreeManager> InProcessCompositorSession::GetAPZCTreeManager() + const { + return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId); +} + +nsIWidget* InProcessCompositorSession::GetWidget() const { return mWidget; } + +void InProcessCompositorSession::Shutdown() { + // Destroy will synchronously wait for the parent to acknowledge shutdown, + // at which point CBP will defer a Release on the compositor thread. We + // can safely release our reference now, and let the destructor run on either + // thread. +#if defined(MOZ_WIDGET_ANDROID) + if (mUiCompositorControllerChild) { + mUiCompositorControllerChild->Destroy(); + mUiCompositorControllerChild = nullptr; + } +#endif // defined(MOZ_WIDGET_ANDROID) + mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; + mCompositorBridgeParent = nullptr; + mCompositorWidget = nullptr; + gfx::GPUProcessManager::Get()->UnregisterInProcessSession(this); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/InProcessCompositorSession.h b/gfx/ipc/InProcessCompositorSession.h new file mode 100644 index 0000000000..5dfee0c913 --- /dev/null +++ b/gfx/ipc/InProcessCompositorSession.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef _include_mozilla_gfx_ipc_InProcessCompositorSession_h_ +#define _include_mozilla_gfx_ipc_InProcessCompositorSession_h_ + +#include "CompositorSession.h" +#include "mozilla/gfx/Point.h" +#include "Units.h" + +class nsIWidget; + +namespace mozilla { +namespace layers { + +class CompositorOptions; +class LayerManager; + +// A CompositorSession where both the child and parent CompositorBridge reside +// in the same process. +class InProcessCompositorSession final : public CompositorSession { + public: + static RefPtr<InProcessCompositorSession> Create( + nsBaseWidget* baseWidget, LayerManager* aLayerManager, + const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, + const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize, uint32_t aNamespace); + + CompositorBridgeParent* GetInProcessBridge() const override; + void SetContentController(GeckoContentController* aController) override; + nsIWidget* GetWidget() const; + RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override; + void Shutdown() override; + + void NotifySessionLost(); + + private: + InProcessCompositorSession(nsBaseWidget* aWidget, + widget::CompositorWidget* aCompositorWidget, + CompositorBridgeChild* aChild, + CompositorBridgeParent* aParent); + + private: + RefPtr<CompositorBridgeParent> mCompositorBridgeParent; + RefPtr<CompositorWidget> mCompositorWidget; +}; + +} // namespace layers +} // namespace mozilla + +#endif // _include_mozilla_gfx_ipc_InProcessCompositorSession_h_ diff --git a/gfx/ipc/PGPU.ipdl b/gfx/ipc/PGPU.ipdl new file mode 100644 index 0000000000..2d9fb59aa6 --- /dev/null +++ b/gfx/ipc/PGPU.ipdl @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include GraphicsMessages; +include MemoryReportTypes; +include HangTypes; +include PrefsTypes; +include protocol PAPZInputBridge; +include protocol PCompositorManager; +include protocol PImageBridge; +include protocol PProfiler; +include protocol PVRGPU; +include protocol PVRManager; +include protocol PVideoBridge; +include protocol PVsyncBridge; +include protocol PUiCompositorController; +include protocol PRemoteDecoderManager; + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) +include protocol PSandboxTesting; +#endif + +include "mozilla/layers/LayersMessageUtils.h"; + +using base::ProcessId from "base/process.h"; +using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h"; +using mozilla::Telemetry::HistogramAccumulation from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::KeyedHistogramAccumulation from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h"; +using mozilla::gfx::Feature from "gfxFeature.h"; +using mozilla::gfx::Fallback from "gfxFallback.h"; +using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; +using mozilla::PDMFactory::MediaCodecsSupported from "PDMFactory.h"; + +namespace mozilla { +namespace gfx { + +struct LayerTreeIdMapping { + LayersId layersId; + ProcessId ownerId; +}; + +// This protocol allows the UI process to talk to the GPU process. There is one +// instance of this protocol, with the GPUParent living on the main thread of +// the GPU process and the GPUChild living on the main thread of the UI process. +sync protocol PGPU +{ + manages PAPZInputBridge; + +parent: + // Sent from the UI process to initialize a new APZ input bridge when a new + // top-level compositor is created. + async PAPZInputBridge(LayersId aLayersId); + + // Sent by the UI process to initiate core settings. + async Init(GfxVarUpdate[] vars, + DevicePrefs devicePrefs, + LayerTreeIdMapping[] mapping, + GfxInfoFeatureStatus[] features); + + async InitCompositorManager(Endpoint<PCompositorManagerParent> endpoint); + async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint); + async InitImageBridge(Endpoint<PImageBridgeParent> endpoint); + async InitVideoBridge(Endpoint<PVideoBridgeParent> endpoint); + async InitVRManager(Endpoint<PVRManagerParent> endpoint); + async InitUiCompositorController(LayersId rootLayerTreeId, Endpoint<PUiCompositorControllerParent> endpoint); + async InitProfiler(Endpoint<PProfilerChild> endpoint); + // Forward GPU process its endpoints to the VR process. + async InitVR(Endpoint<PVRGPUChild> endpoint); + // Called to update a gfx variable. + async UpdateVar(GfxVarUpdate var); + + async PreferenceUpdate(Pref pref); + + // Create a new content-process compositor bridge. + async NewContentCompositorManager(Endpoint<PCompositorManagerParent> endpoint); + async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint); + async NewContentVRManager(Endpoint<PVRManagerParent> endpoint); + async NewContentRemoteDecoderManager(Endpoint<PRemoteDecoderManagerParent> endpoint); + + // Called to notify the GPU process of who owns a layersId. + sync AddLayerTreeIdMapping(LayerTreeIdMapping mapping); + async RemoveLayerTreeIdMapping(LayerTreeIdMapping mapping); + + // Request the current DeviceStatus from the GPU process. This blocks until + // one is available (i.e., Init has completed). + sync GetDeviceStatus() returns (GPUDeviceData status); + + // Request to simulate device reset and to get the updated DeviceStatus from + // the GPU process. This blocks until one is available (i.e., Init has completed). + sync SimulateDeviceReset() returns (GPUDeviceData status); + + // Have a message be broadcasted to the GPU process by the GPU process + // observer service. + async NotifyGpuObservers(nsCString aTopic); + + async RequestMemoryReport(uint32_t generation, + bool anonymize, + bool minimizeMemoryUsage, + FileDescriptor? DMDFile) + returns (uint32_t aGeneration); + + async ShutdownVR(); + + // Functions supporting PerfStats data collection. + async UpdatePerfStatsCollectionMask(uint64_t aMask); + async CollectPerfStatsJSON() returns (nsCString aStats); + +#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) + async InitSandboxTesting(Endpoint<PSandboxTestingChild> aEndpoint); +#endif + +child: + // Sent when the GPU process has initialized devices. This occurs once, after + // Init(). + async InitComplete(GPUDeviceData data); + + // Sent when APZ detects checkerboarding and apz checkerboard reporting is enabled. + async ReportCheckerboard(uint32_t severity, nsCString log); + + // Graphics errors, analogous to PContent::GraphicsError + async GraphicsError(nsCString aError); + + async InitCrashReporter(NativeThreadId threadId); + + async CreateVRProcess(); + async ShutdownVRProcess(); + + // Have a message be broadcasted to the UI process by the UI process + // observer service. + async NotifyUiObservers(nsCString aTopic); + + // Messages for reporting telemetry to the UI process. + async AccumulateChildHistograms(HistogramAccumulation[] accumulations); + async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations); + async UpdateChildScalars(ScalarAction[] actions); + async UpdateChildKeyedScalars(KeyedScalarAction[] actions); + async RecordChildEvents(ChildEventData[] events); + async RecordDiscardedData(DiscardedData data); + + async NotifyDeviceReset(GPUDeviceData status); + + async AddMemoryReport(MemoryReport aReport); + + // Update the UI process after a feature's status has changed. This is used + // outside of the normal startup flow. + async UpdateFeature(Feature aFeature, FeatureFailure aChange); + + // Notify about:support/Telemetry that a fallback occurred. + async UsedFallback(Fallback aFallback, nsCString message); + + async BHRThreadHang(HangDetails aDetails); + + // Update the cached list of codec supported following a check in the + // GPU parent. + async UpdateMediaCodecsSupported(MediaCodecsSupported aSupported); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/PVsyncBridge.ipdl b/gfx/ipc/PVsyncBridge.ipdl new file mode 100644 index 0000000000..505cbb019d --- /dev/null +++ b/gfx/ipc/PVsyncBridge.ipdl @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; +using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h"; +using mozilla::VsyncEvent from "mozilla/VsyncDispatcher.h"; + +namespace mozilla { +namespace gfx { + +// This protocol only serves one purpose: deliver vsync notifications from a +// dedicated thread in the UI process to the compositor thread in the +// compositor process. The child side exists in the UI process, and the +// parent side in the GPU process. +sync protocol PVsyncBridge +{ +parent: + async NotifyVsync(VsyncEvent vsync, LayersId layersId); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/RemoteCompositorSession.cpp b/gfx/ipc/RemoteCompositorSession.cpp new file mode 100644 index 0000000000..d11a9628cf --- /dev/null +++ b/gfx/ipc/RemoteCompositorSession.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RemoteCompositorSession.h" +#include "gfxPlatform.h" +#include "mozilla/VsyncDispatcher.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/GeckoContentController.h" +#include "mozilla/Unused.h" +#include "nsBaseWidget.h" +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/layers/UiCompositorControllerChild.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +namespace mozilla { +namespace layers { + +using namespace gfx; +using namespace widget; + +RemoteCompositorSession::RemoteCompositorSession( + nsBaseWidget* aWidget, CompositorBridgeChild* aChild, + CompositorWidgetDelegate* aWidgetDelegate, APZCTreeManagerChild* aAPZ, + const LayersId& aRootLayerTreeId) + : CompositorSession(aWidget, aWidgetDelegate, aChild, aRootLayerTreeId), + mAPZ(aAPZ) { + MOZ_ASSERT(!gfxPlatform::IsHeadless()); + GPUProcessManager::Get()->RegisterRemoteProcessSession(this); + if (mAPZ) { + mAPZ->SetCompositorSession(this); + } +} + +RemoteCompositorSession::~RemoteCompositorSession() { + // This should have been shutdown first. + MOZ_ASSERT(!mCompositorBridgeChild); +#if defined(MOZ_WIDGET_ANDROID) + MOZ_ASSERT(!mUiCompositorControllerChild); +#endif // defined(MOZ_WIDGET_ANDROID) +} + +void RemoteCompositorSession::NotifySessionLost() { + // Re-entrancy should be impossible: when we are being notified of a lost + // session, we have by definition not shut down yet. We will shutdown, but + // then will be removed from the notification list. + mWidget->NotifyCompositorSessionLost(this); +} + +CompositorBridgeParent* RemoteCompositorSession::GetInProcessBridge() const { + return nullptr; +} + +void RemoteCompositorSession::SetContentController( + GeckoContentController* aController) { + mContentController = aController; + mCompositorBridgeChild->SendPAPZConstructor(new APZChild(aController), + LayersId{0}); +} + +GeckoContentController* RemoteCompositorSession::GetContentController() { + return mContentController.get(); +} + +nsIWidget* RemoteCompositorSession::GetWidget() const { return mWidget; } + +RefPtr<IAPZCTreeManager> RemoteCompositorSession::GetAPZCTreeManager() const { + return mAPZ; +} + +void RemoteCompositorSession::Shutdown() { + mContentController = nullptr; + if (mAPZ) { + mAPZ->SetCompositorSession(nullptr); + mAPZ->Destroy(); + } + mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; + mCompositorWidgetDelegate = nullptr; + mWidget = nullptr; +#if defined(MOZ_WIDGET_ANDROID) + if (mUiCompositorControllerChild) { + mUiCompositorControllerChild->Destroy(); + mUiCompositorControllerChild = nullptr; + } +#endif // defined(MOZ_WIDGET_ANDROID) + GPUProcessManager::Get()->UnregisterRemoteProcessSession(this); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/RemoteCompositorSession.h b/gfx/ipc/RemoteCompositorSession.h new file mode 100644 index 0000000000..d55d56e07a --- /dev/null +++ b/gfx/ipc/RemoteCompositorSession.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef include_mozilla_gfx_ipc_RemoteCompositorSession_h +#define include_mozilla_gfx_ipc_RemoteCompositorSession_h + +#include "CompositorSession.h" +#include "mozilla/gfx/Point.h" +#include "Units.h" + +class nsIWidget; + +namespace mozilla { +namespace layers { + +class APZCTreeManagerChild; + +class RemoteCompositorSession final : public CompositorSession { + public: + RemoteCompositorSession(nsBaseWidget* aWidget, CompositorBridgeChild* aChild, + CompositorWidgetDelegate* aWidgetDelegate, + APZCTreeManagerChild* aAPZ, + const LayersId& aRootLayerTreeId); + virtual ~RemoteCompositorSession(); + + CompositorBridgeParent* GetInProcessBridge() const override; + void SetContentController(GeckoContentController* aController) override; + GeckoContentController* GetContentController(); + nsIWidget* GetWidget() const; + RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override; + void Shutdown() override; + + void NotifySessionLost(); + + private: + RefPtr<APZCTreeManagerChild> mAPZ; + RefPtr<GeckoContentController> mContentController; +}; + +} // namespace layers +} // namespace mozilla + +#endif // include_mozilla_gfx_ipc_RemoteCompositorSession_h diff --git a/gfx/ipc/SharedDIB.cpp b/gfx/ipc/SharedDIB.cpp new file mode 100644 index 0000000000..7da3a39b3b --- /dev/null +++ b/gfx/ipc/SharedDIB.cpp @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SharedDIB.h" + +namespace mozilla { +namespace gfx { + +SharedDIB::SharedDIB() : mShMem(nullptr) {} + +SharedDIB::~SharedDIB() { Close(); } + +nsresult SharedDIB::Create(uint32_t aSize) { + Close(); + + mShMem = new base::SharedMemory(); + if (!mShMem || !mShMem->Create(aSize)) return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +bool SharedDIB::IsValid() { return mShMem && mShMem->IsValid(); } + +nsresult SharedDIB::Close() { + delete mShMem; + + mShMem = nullptr; + + return NS_OK; +} + +nsresult SharedDIB::Attach(Handle aHandle, uint32_t aSize) { + Close(); + + mShMem = new base::SharedMemory(aHandle, false); + if (!mShMem) return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +nsresult SharedDIB::ShareToProcess(base::ProcessId aTargetPid, + Handle* aNewHandle) { + if (!mShMem) return NS_ERROR_UNEXPECTED; + + if (!mShMem->ShareToProcess(aTargetPid, aNewHandle)) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/SharedDIB.h b/gfx/ipc/SharedDIB.h new file mode 100644 index 0000000000..d957bb8269 --- /dev/null +++ b/gfx/ipc/SharedDIB.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef gfx_SharedDIB_h__ +#define gfx_SharedDIB_h__ + +#include "base/shared_memory.h" +#include "nscore.h" + +namespace mozilla { +namespace gfx { + +class SharedDIB { + public: + typedef base::SharedMemoryHandle Handle; + + static const uint32_t kBytesPerPixel = 4; + + public: + SharedDIB(); + ~SharedDIB(); + + // Create and allocate a new shared dib. + nsresult Create(uint32_t aSize); + + // Destroy or release resources associated with this dib. + nsresult Close(); + + // Returns true if this object contains a valid dib. + bool IsValid(); + + // Wrap a new shared dib around allocated shared memory. Note aHandle must + // point to a memory section large enough to hold a dib of size aSize, + // otherwise this will fail. + nsresult Attach(Handle aHandle, uint32_t aSize); + + // Returns a SharedMemoryHandle suitable for sharing with another process. + nsresult ShareToProcess(base::ProcessId aTargetPid, Handle* aNewHandle); + + protected: + base::SharedMemory* mShMem; +}; + +} // namespace gfx +} // namespace mozilla + +#endif diff --git a/gfx/ipc/SharedDIBSurface.cpp b/gfx/ipc/SharedDIBSurface.cpp new file mode 100644 index 0000000000..c1ee7239df --- /dev/null +++ b/gfx/ipc/SharedDIBSurface.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SharedDIBSurface.h" + +#include "cairo.h" + +namespace mozilla { +namespace gfx { + +static const cairo_user_data_key_t SHAREDDIB_KEY = {0}; + +bool SharedDIBSurface::Create(HDC adc, uint32_t aWidth, uint32_t aHeight, + bool aTransparent) { + nsresult rv = mSharedDIB.Create(adc, aWidth, aHeight, aTransparent); + if (NS_FAILED(rv) || !mSharedDIB.IsValid()) return false; + + InitSurface(aWidth, aHeight, aTransparent); + return true; +} + +bool SharedDIBSurface::Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight, + bool aTransparent) { + nsresult rv = mSharedDIB.Attach(aHandle, aWidth, aHeight, aTransparent); + if (NS_FAILED(rv) || !mSharedDIB.IsValid()) return false; + + InitSurface(aWidth, aHeight, aTransparent); + return true; +} + +void SharedDIBSurface::InitSurface(uint32_t aWidth, uint32_t aHeight, + bool aTransparent) { + long stride = long(aWidth * SharedDIB::kBytesPerPixel); + unsigned char* data = reinterpret_cast<unsigned char*>(mSharedDIB.GetBits()); + + gfxImageFormat format = aTransparent ? SurfaceFormat::A8R8G8B8_UINT32 + : SurfaceFormat::X8R8G8B8_UINT32; + + gfxImageSurface::InitWithData(data, IntSize(aWidth, aHeight), stride, format); + + cairo_surface_set_user_data(mSurface, &SHAREDDIB_KEY, this, nullptr); +} + +bool SharedDIBSurface::IsSharedDIBSurface(gfxASurface* aSurface) { + return aSurface && aSurface->GetType() == gfxSurfaceType::Image && + aSurface->GetData(&SHAREDDIB_KEY); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/SharedDIBSurface.h b/gfx/ipc/SharedDIBSurface.h new file mode 100644 index 0000000000..412aeb89c5 --- /dev/null +++ b/gfx/ipc/SharedDIBSurface.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_gfx_SharedDIBSurface_h +#define mozilla_gfx_SharedDIBSurface_h + +#include "gfxImageSurface.h" +#include "SharedDIBWin.h" + +#include <windows.h> + +namespace mozilla { +namespace gfx { + +/** + * A SharedDIBSurface owns an underlying SharedDIBWin. + */ +class SharedDIBSurface : public gfxImageSurface { + public: + typedef base::SharedMemoryHandle Handle; + + SharedDIBSurface() {} + ~SharedDIBSurface() {} + + /** + * Create this image surface backed by shared memory. + */ + bool Create(HDC adc, uint32_t aWidth, uint32_t aHeight, bool aTransparent); + + /** + * Attach this surface to shared memory from another process. + */ + bool Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight, + bool aTransparent); + + /** + * After drawing to a surface via GDI, GDI must be flushed before the bitmap + * is valid. + */ + void Flush() { ::GdiFlush(); } + + HDC GetHDC() { return mSharedDIB.GetHDC(); } + + nsresult ShareToProcess(base::ProcessId aTargetPid, Handle* aNewHandle) { + return mSharedDIB.ShareToProcess(aTargetPid, aNewHandle); + } + + static bool IsSharedDIBSurface(gfxASurface* aSurface); + + private: + SharedDIBWin mSharedDIB; + + void InitSurface(uint32_t aWidth, uint32_t aHeight, bool aTransparent); +}; + +} // namespace gfx +} // namespace mozilla + +#endif // mozilla_gfx_SharedDIBSurface_h diff --git a/gfx/ipc/SharedDIBWin.cpp b/gfx/ipc/SharedDIBWin.cpp new file mode 100644 index 0000000000..9669cb9491 --- /dev/null +++ b/gfx/ipc/SharedDIBWin.cpp @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "SharedDIBWin.h" +#include "gfxAlphaRecovery.h" +#include "nsMathUtils.h" +#include "nsDebug.h" + +namespace mozilla { +namespace gfx { + +static const uint32_t kByteAlign = 1 << gfxAlphaRecovery::GoodAlignmentLog2(); +static const uint32_t kHeaderBytes = + (uint32_t(sizeof(BITMAPV4HEADER)) + kByteAlign - 1) & ~(kByteAlign - 1); + +SharedDIBWin::SharedDIBWin() + : mSharedHdc(nullptr), mSharedBmp(nullptr), mOldObj(nullptr) {} + +SharedDIBWin::~SharedDIBWin() { Close(); } + +nsresult SharedDIBWin::Close() { + if (mSharedHdc && mOldObj) ::SelectObject(mSharedHdc, mOldObj); + + if (mSharedHdc) ::DeleteObject(mSharedHdc); + + if (mSharedBmp) ::DeleteObject(mSharedBmp); + + mSharedHdc = nullptr; + mOldObj = mSharedBmp = nullptr; + + SharedDIB::Close(); + + return NS_OK; +} + +nsresult SharedDIBWin::Create(HDC aHdc, uint32_t aWidth, uint32_t aHeight, + bool aTransparent) { + Close(); + + // create the offscreen shared dib + BITMAPV4HEADER bmih; + uint32_t size = SetupBitmapHeader(aWidth, aHeight, aTransparent, &bmih); + + nsresult rv = SharedDIB::Create(size); + if (NS_FAILED(rv)) return rv; + + if (NS_FAILED(SetupSurface(aHdc, &bmih))) { + Close(); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult SharedDIBWin::Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight, + bool aTransparent) { + Close(); + + BITMAPV4HEADER bmih; + SetupBitmapHeader(aWidth, aHeight, aTransparent, &bmih); + + nsresult rv = SharedDIB::Attach(aHandle, 0); + if (NS_FAILED(rv)) return rv; + + if (NS_FAILED(SetupSurface(nullptr, &bmih))) { + Close(); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +uint32_t SharedDIBWin::SetupBitmapHeader(uint32_t aWidth, uint32_t aHeight, + bool aTransparent, + BITMAPV4HEADER* aHeader) { + // D3D cannot handle an offscreen memory that pitch (SysMemPitch) is negative. + // So we create top-to-bottom DIB. + memset((void*)aHeader, 0, sizeof(BITMAPV4HEADER)); + aHeader->bV4Size = sizeof(BITMAPV4HEADER); + aHeader->bV4Width = aWidth; + aHeader->bV4Height = -LONG(aHeight); // top-to-buttom DIB + aHeader->bV4Planes = 1; + aHeader->bV4BitCount = 32; + aHeader->bV4V4Compression = BI_BITFIELDS; + aHeader->bV4RedMask = 0x00FF0000; + aHeader->bV4GreenMask = 0x0000FF00; + aHeader->bV4BlueMask = 0x000000FF; + + if (aTransparent) aHeader->bV4AlphaMask = 0xFF000000; + + return (kHeaderBytes + + (-aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel)); +} + +nsresult SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER* aHdr) { + mSharedHdc = ::CreateCompatibleDC(aHdc); + + if (!mSharedHdc) return NS_ERROR_FAILURE; + + mSharedBmp = + ::CreateDIBSection(mSharedHdc, (BITMAPINFO*)aHdr, DIB_RGB_COLORS, + &mBitmapBits, mShMem->GetHandle(), kHeaderBytes); + if (!mSharedBmp) return NS_ERROR_FAILURE; + + mOldObj = SelectObject(mSharedHdc, mSharedBmp); + + return NS_OK; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/SharedDIBWin.h b/gfx/ipc/SharedDIBWin.h new file mode 100644 index 0000000000..06580af2df --- /dev/null +++ b/gfx/ipc/SharedDIBWin.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef gfx_SharedDIBWin_h__ +#define gfx_SharedDIBWin_h__ + +#include <windows.h> + +#include "SharedDIB.h" + +namespace mozilla { +namespace gfx { + +class SharedDIBWin : public SharedDIB { + public: + SharedDIBWin(); + ~SharedDIBWin(); + + // Allocate a new win32 dib section compatible with an hdc. The dib will + // be selected into the hdc on return. + nsresult Create(HDC aHdc, uint32_t aWidth, uint32_t aHeight, + bool aTransparent); + + // Wrap a dib section around an existing shared memory object. aHandle should + // point to a section large enough for the dib's memory, otherwise this call + // will fail. + nsresult Attach(Handle aHandle, uint32_t aWidth, uint32_t aHeight, + bool aTransparent); + + // Destroy or release resources associated with this dib. + nsresult Close(); + + // Return the HDC of the shared dib. + HDC GetHDC() { return mSharedHdc; } + + // Return the bitmap bits. + void* GetBits() { return mBitmapBits; } + + private: + HDC mSharedHdc; + HBITMAP mSharedBmp; + HGDIOBJ mOldObj; + void* mBitmapBits; + + uint32_t SetupBitmapHeader(uint32_t aWidth, uint32_t aHeight, + bool aTransparent, BITMAPV4HEADER* aHeader); + nsresult SetupSurface(HDC aHdc, BITMAPV4HEADER* aHdr); +}; + +} // namespace gfx +} // namespace mozilla + +#endif diff --git a/gfx/ipc/VsyncBridgeChild.cpp b/gfx/ipc/VsyncBridgeChild.cpp new file mode 100644 index 0000000000..13417cab90 --- /dev/null +++ b/gfx/ipc/VsyncBridgeChild.cpp @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "VsyncBridgeChild.h" +#include "VsyncIOThreadHolder.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/ipc/Endpoint.h" + +namespace mozilla { +namespace gfx { + +VsyncBridgeChild::VsyncBridgeChild(RefPtr<VsyncIOThreadHolder> aThread, + const uint64_t& aProcessToken) + : mThread(aThread), mProcessToken(aProcessToken) {} + +VsyncBridgeChild::~VsyncBridgeChild() = default; + +/* static */ +RefPtr<VsyncBridgeChild> VsyncBridgeChild::Create( + RefPtr<VsyncIOThreadHolder> aThread, const uint64_t& aProcessToken, + Endpoint<PVsyncBridgeChild>&& aEndpoint) { + RefPtr<VsyncBridgeChild> child = new VsyncBridgeChild(aThread, aProcessToken); + + RefPtr<nsIRunnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeChild>&&>( + "gfx::VsyncBridgeChild::Open", child, &VsyncBridgeChild::Open, + std::move(aEndpoint)); + aThread->GetThread()->Dispatch(task.forget(), nsIThread::DISPATCH_NORMAL); + + return child; +} + +void VsyncBridgeChild::Open(Endpoint<PVsyncBridgeChild>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + // The GPU Process Manager might be gone if we receive ActorDestroy very + // late in shutdown. + if (GPUProcessManager* gpm = GPUProcessManager::Get()) + gpm->NotifyRemoteActorDestroyed(mProcessToken); + return; + } + + // Last reference is freed in DeallocPVsyncBridgeChild. + AddRef(); +} + +class NotifyVsyncTask : public Runnable { + public: + NotifyVsyncTask(RefPtr<VsyncBridgeChild> aVsyncBridge, + const VsyncEvent& aVsync, const layers::LayersId& aLayersId) + : Runnable("gfx::NotifyVsyncTask"), + mVsyncBridge(aVsyncBridge), + mVsync(aVsync), + mLayersId(aLayersId) {} + + NS_IMETHOD Run() override { + mVsyncBridge->NotifyVsyncImpl(mVsync, mLayersId); + return NS_OK; + } + + private: + RefPtr<VsyncBridgeChild> mVsyncBridge; + VsyncEvent mVsync; + layers::LayersId mLayersId; +}; + +bool VsyncBridgeChild::IsOnVsyncIOThread() const { + return mThread->IsOnCurrentThread(); +} + +void VsyncBridgeChild::NotifyVsync(const VsyncEvent& aVsync, + const layers::LayersId& aLayersId) { + // This should be on the Vsync thread (not the Vsync I/O thread). + MOZ_ASSERT(!IsOnVsyncIOThread()); + + RefPtr<NotifyVsyncTask> task = new NotifyVsyncTask(this, aVsync, aLayersId); + mThread->Dispatch(task.forget()); +} + +void VsyncBridgeChild::NotifyVsyncImpl(const VsyncEvent& aVsync, + const layers::LayersId& aLayersId) { + // This should be on the Vsync I/O thread. + MOZ_ASSERT(IsOnVsyncIOThread()); + + if (!mProcessToken) { + return; + } + SendNotifyVsync(aVsync, aLayersId); +} + +void VsyncBridgeChild::Close() { + if (!IsOnVsyncIOThread()) { + mThread->Dispatch(NewRunnableMethod("gfx::VsyncBridgeChild::Close", this, + &VsyncBridgeChild::Close)); + return; + } + + // We clear mProcessToken when the channel is closed. + if (!mProcessToken) { + return; + } + + // Clear the process token so we don't notify the GPUProcessManager. It + // already knows we're closed since it manually called Close, and in fact the + // GPM could have already been destroyed during shutdown. + mProcessToken = 0; + + // Close the underlying IPC channel. + PVsyncBridgeChild::Close(); +} + +void VsyncBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + if (mProcessToken) { + GPUProcessManager::Get()->NotifyRemoteActorDestroyed(mProcessToken); + mProcessToken = 0; + } +} + +void VsyncBridgeChild::ActorDealloc() { Release(); } + +void VsyncBridgeChild::ProcessingError(Result aCode, const char* aReason) { + MOZ_RELEASE_ASSERT(aCode == MsgDropped, + "Processing error in VsyncBridgeChild"); +} + +void VsyncBridgeChild::HandleFatalError(const char* aMsg) const { + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid()); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncBridgeChild.h b/gfx/ipc/VsyncBridgeChild.h new file mode 100644 index 0000000000..b85e90453a --- /dev/null +++ b/gfx/ipc/VsyncBridgeChild.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef include_gfx_ipc_VsyncBridgeChild_h +#define include_gfx_ipc_VsyncBridgeChild_h + +#include "mozilla/RefPtr.h" +#include "mozilla/gfx/PVsyncBridgeChild.h" + +namespace mozilla { +namespace gfx { + +class VsyncIOThreadHolder; + +class VsyncBridgeChild final : public PVsyncBridgeChild { + friend class NotifyVsyncTask; + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeChild) + + static RefPtr<VsyncBridgeChild> Create( + RefPtr<VsyncIOThreadHolder> aThread, const uint64_t& aProcessToken, + Endpoint<PVsyncBridgeChild>&& aEndpoint); + + void Close(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + void ActorDealloc() override; + void ProcessingError(Result aCode, const char* aReason) override; + + void NotifyVsync(const VsyncEvent& aVsync, const layers::LayersId& aLayersId); + + void HandleFatalError(const char* aMsg) const override; + + private: + VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken); + virtual ~VsyncBridgeChild(); + + void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint); + + void NotifyVsyncImpl(const VsyncEvent& aVsync, + const layers::LayersId& aLayersId); + + bool IsOnVsyncIOThread() const; + + private: + RefPtr<VsyncIOThreadHolder> mThread; + uint64_t mProcessToken; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // include_gfx_ipc_VsyncBridgeChild_h diff --git a/gfx/ipc/VsyncBridgeParent.cpp b/gfx/ipc/VsyncBridgeParent.cpp new file mode 100644 index 0000000000..6142de417b --- /dev/null +++ b/gfx/ipc/VsyncBridgeParent.cpp @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "VsyncBridgeParent.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorThread.h" + +using mozilla::layers::CompositorBridgeParent; +using mozilla::layers::CompositorThreadHolder; + +namespace mozilla { +namespace gfx { + +RefPtr<VsyncBridgeParent> VsyncBridgeParent::Start( + Endpoint<PVsyncBridgeParent>&& aEndpoint) { + RefPtr<VsyncBridgeParent> parent = new VsyncBridgeParent(); + + RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeParent>&&>( + "gfx::VsyncBridgeParent::Open", parent, &VsyncBridgeParent::Open, + std::move(aEndpoint)); + layers::CompositorThread()->Dispatch(task.forget()); + + return parent; +} + +VsyncBridgeParent::VsyncBridgeParent() : mOpen(false) { + MOZ_COUNT_CTOR(VsyncBridgeParent); + mCompositorThreadRef = CompositorThreadHolder::GetSingleton(); +} + +VsyncBridgeParent::~VsyncBridgeParent() { MOZ_COUNT_DTOR(VsyncBridgeParent); } + +void VsyncBridgeParent::Open(Endpoint<PVsyncBridgeParent>&& aEndpoint) { + if (!aEndpoint.Bind(this)) { + // We can't recover from this. + MOZ_CRASH("Failed to bind VsyncBridgeParent to endpoint"); + } + AddRef(); + mOpen = true; +} + +mozilla::ipc::IPCResult VsyncBridgeParent::RecvNotifyVsync( + const VsyncEvent& aVsync, const LayersId& aLayersId) { + CompositorBridgeParent::NotifyVsync(aVsync, aLayersId); + return IPC_OK(); +} + +void VsyncBridgeParent::Shutdown() { + if (!CompositorThreadHolder::IsInCompositorThread()) { + layers::CompositorThread()->Dispatch( + NewRunnableMethod("gfx::VsyncBridgeParent::ShutdownImpl", this, + &VsyncBridgeParent::ShutdownImpl)); + return; + } + + ShutdownImpl(); +} + +void VsyncBridgeParent::ShutdownImpl() { + if (mOpen) { + Close(); + mOpen = false; + } +} + +void VsyncBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { + mOpen = false; + mCompositorThreadRef = nullptr; +} + +void VsyncBridgeParent::ActorDealloc() { Release(); } + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncBridgeParent.h b/gfx/ipc/VsyncBridgeParent.h new file mode 100644 index 0000000000..5c37b7e689 --- /dev/null +++ b/gfx/ipc/VsyncBridgeParent.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef include_gfx_ipc_VsyncBridgeParent_h +#define include_gfx_ipc_VsyncBridgeParent_h + +#include "mozilla/RefPtr.h" +#include "mozilla/gfx/PVsyncBridgeParent.h" + +namespace mozilla { +namespace layers { +class CompositorThreadHolder; +} // namespace layers + +namespace gfx { + +class VsyncBridgeParent final : public PVsyncBridgeParent { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeParent) + + static RefPtr<VsyncBridgeParent> Start( + Endpoint<PVsyncBridgeParent>&& aEndpoint); + + mozilla::ipc::IPCResult RecvNotifyVsync(const VsyncEvent& aVsync, + const LayersId& aLayersId); + void ActorDestroy(ActorDestroyReason aWhy) override; + void ActorDealloc() override; + + void Shutdown(); + + private: + VsyncBridgeParent(); + ~VsyncBridgeParent(); + + void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint); + void ShutdownImpl(); + + private: + bool mOpen; + RefPtr<layers::CompositorThreadHolder> mCompositorThreadRef; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // include_gfx_ipc_VsyncBridgeParent_h diff --git a/gfx/ipc/VsyncIOThreadHolder.cpp b/gfx/ipc/VsyncIOThreadHolder.cpp new file mode 100644 index 0000000000..c627ffa397 --- /dev/null +++ b/gfx/ipc/VsyncIOThreadHolder.cpp @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VsyncIOThreadHolder.h" + +#include "mozilla/SchedulerGroup.h" + +namespace mozilla { +namespace gfx { + +VsyncIOThreadHolder::VsyncIOThreadHolder() { + MOZ_COUNT_CTOR(VsyncIOThreadHolder); +} + +VsyncIOThreadHolder::~VsyncIOThreadHolder() { + MOZ_COUNT_DTOR(VsyncIOThreadHolder); + + if (!mThread) { + return; + } + + if (NS_IsMainThread()) { + mThread->AsyncShutdown(); + } else { + SchedulerGroup::Dispatch( + TaskCategory::Other, + NewRunnableMethod("nsIThread::AsyncShutdown", mThread, + &nsIThread::AsyncShutdown)); + } +} + +bool VsyncIOThreadHolder::Start() { + nsresult rv = NS_NewNamedThread("VsyncIOThread", getter_AddRefs(mThread)); + return NS_SUCCEEDED(rv); +} + +RefPtr<nsIThread> VsyncIOThreadHolder::GetThread() const { return mThread; } + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncIOThreadHolder.h b/gfx/ipc/VsyncIOThreadHolder.h new file mode 100644 index 0000000000..8158834cab --- /dev/null +++ b/gfx/ipc/VsyncIOThreadHolder.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_gfx_ipc_VsyncIOThreadHolder_h +#define mozilla_gfx_ipc_VsyncIOThreadHolder_h + +#include "mozilla/RefPtr.h" +#include "nsIThread.h" +#include "nsThreadUtils.h" + +namespace mozilla { +namespace gfx { + +class VsyncIOThreadHolder final { + public: + VsyncIOThreadHolder(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncIOThreadHolder) + + bool Start(); + + RefPtr<nsIThread> GetThread() const; + + bool IsOnCurrentThread() const { return mThread->IsOnCurrentThread(); } + + void Dispatch(already_AddRefed<nsIRunnable> task) { + mThread->Dispatch(std::move(task), NS_DISPATCH_NORMAL); + } + + private: + ~VsyncIOThreadHolder(); + + private: + RefPtr<nsIThread> mThread; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // mozilla_gfx_ipc_VsyncIOThreadHolder_h diff --git a/gfx/ipc/moz.build b/gfx/ipc/moz.build new file mode 100644 index 0000000000..85744c81be --- /dev/null +++ b/gfx/ipc/moz.build @@ -0,0 +1,90 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Core", "Graphics: Layers") + +EXPORTS.mozilla += ["D3DMessageUtils.h", "GfxMessageUtils.h"] + +EXPORTS.mozilla.gfx += [ + "CrossProcessPaint.h", + "GPUChild.h", + "GPUParent.h", + "GPUProcessHost.h", + "GPUProcessImpl.h", + "GPUProcessListener.h", + "GPUProcessManager.h", + "SharedDIB.h", + "VsyncBridgeChild.h", + "VsyncBridgeParent.h", + "VsyncIOThreadHolder.h", +] + +EXPORTS.mozilla.layers += [ + "CompositorOptions.h", + "CompositorSession.h", + "InProcessCompositorSession.h", + "RemoteCompositorSession.h", +] + +EXPORTS.mozilla.widget += [ + "CompositorWidgetVsyncObserver.h", +] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": + EXPORTS.mozilla.gfx += [ + "SharedDIBSurface.h", + "SharedDIBWin.h", + ] + UNIFIED_SOURCES += [ + "SharedDIBSurface.cpp", + "SharedDIBWin.cpp", + ] + +UNIFIED_SOURCES += [ + "CompositorSession.cpp", + "CompositorWidgetVsyncObserver.cpp", + "CrossProcessPaint.cpp", + "D3DMessageUtils.cpp", + "GPUChild.cpp", + "GPUProcessHost.cpp", + "GPUProcessImpl.cpp", + "GPUProcessManager.cpp", + "InProcessCompositorSession.cpp", + "RemoteCompositorSession.cpp", + "SharedDIB.cpp", + "VsyncBridgeChild.cpp", + "VsyncBridgeParent.cpp", + "VsyncIOThreadHolder.cpp", +] + +SOURCES += [ + "GPUParent.cpp", +] + +IPDL_SOURCES = [ + "GraphicsMessages.ipdlh", + "PVsyncBridge.ipdl", +] + +PREPROCESSED_IPDL_SOURCES += [ + "PGPU.ipdl", +] + +LOCAL_INCLUDES += [ + "/dom/ipc", + "/toolkit/crashreporter", + "/xpcom/threads", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" + +CXXFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"] +CXXFLAGS += CONFIG["TK_CFLAGS"] + +LOCAL_INCLUDES += CONFIG["SKIA_INCLUDES"] |