diff options
Diffstat (limited to 'gfx/ipc')
38 files changed, 5368 insertions, 0 deletions
diff --git a/gfx/ipc/CompositorSession.cpp b/gfx/ipc/CompositorSession.cpp new file mode 100644 index 000000000..b200b341c --- /dev/null +++ b/gfx/ipc/CompositorSession.cpp @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +namespace mozilla { +namespace layers { + +using namespace gfx; +using namespace widget; + + +CompositorSession::CompositorSession(CompositorWidgetDelegate* aDelegate, + CompositorBridgeChild* aChild, + const uint64_t& aRootLayerTreeId) + : mCompositorWidgetDelegate(aDelegate), + mCompositorBridgeChild(aChild), + mRootLayerTreeId(aRootLayerTreeId) +{ +} + +CompositorSession::~CompositorSession() +{ +} + +CompositorBridgeChild* +CompositorSession::GetCompositorBridgeChild() +{ + return mCompositorBridgeChild; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/CompositorSession.h b/gfx/ipc/CompositorSession.h new file mode 100644 index 000000000..6205725c5 --- /dev/null +++ b/gfx/ipc/CompositorSession.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +class nsIWidget; + +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 bool Reset(const nsTArray<LayersBackend>& aBackendHints, + TextureFactoryIdentifier* aOutIdentifier) = 0; + + 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. + uint64_t RootLayerTreeId() const { + return mRootLayerTreeId; + } + +protected: + CompositorSession(CompositorWidgetDelegate* aDelegate, + CompositorBridgeChild* aChild, + const uint64_t& aRootLayerTreeId); + virtual ~CompositorSession(); + +protected: + CompositorWidgetDelegate* mCompositorWidgetDelegate; + RefPtr<CompositorBridgeChild> mCompositorBridgeChild; + uint64_t mRootLayerTreeId; + +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 000000000..767dc9da4 --- /dev/null +++ b/gfx/ipc/CompositorWidgetVsyncObserver.cpp @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 uint64_t& aRootLayerTreeId) + : mVsyncBridge(aVsyncBridge), + mRootLayerTreeId(aRootLayerTreeId) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); +} + +bool +CompositorWidgetVsyncObserver::NotifyVsync(TimeStamp aTimeStamp) +{ + // Vsync notifications should only arrive on the vsync thread. + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + + mVsyncBridge->NotifyVsync(aTimeStamp, mRootLayerTreeId); + return true; +} + +} // namespace widget +} // namespace mozilla diff --git a/gfx/ipc/CompositorWidgetVsyncObserver.h b/gfx/ipc/CompositorWidgetVsyncObserver.h new file mode 100644 index 000000000..c13662474 --- /dev/null +++ b/gfx/ipc/CompositorWidgetVsyncObserver.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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/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 uint64_t& aRootLayerTreeId); + + bool NotifyVsync(TimeStamp aVsyncTimestamp) override; + + private: + RefPtr<VsyncBridgeChild> mVsyncBridge; + uint64_t mRootLayerTreeId; +}; + +} // namespace widget +} // namespace mozilla + +#endif // mozilla_gfx_ipc_CompositorWidgetVsyncObserver_h diff --git a/gfx/ipc/D3DMessageUtils.cpp b/gfx/ipc/D3DMessageUtils.cpp new file mode 100644 index 000000000..f3895fdae --- /dev/null +++ b/gfx/ipc/D3DMessageUtils.cpp @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 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); +#else + MOZ_ASSERT_UNREACHABLE("DxgiAdapterDesc is Windows-only"); +#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; + } +#else + MOZ_ASSERT_UNREACHABLE("DxgiAdapterDesc is Windows-only"); +#endif + return false; +} + +} // namespace IPC diff --git a/gfx/ipc/D3DMessageUtils.h b/gfx/ipc/D3DMessageUtils.h new file mode 100644 index 000000000..0dc9b4843 --- /dev/null +++ b/gfx/ipc/D3DMessageUtils.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 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 000000000..72328ac0b --- /dev/null +++ b/gfx/ipc/GPUChild.cpp @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "gfxConfig.h" +#include "gfxPrefs.h" +#include "GPUProcessHost.h" +#include "GPUProcessManager.h" +#include "mozilla/Telemetry.h" +#include "mozilla/dom/CheckerboardReportService.h" +#include "mozilla/gfx/gfxVars.h" +#if defined(XP_WIN) +# include "mozilla/gfx/DeviceManagerDx.h" +#endif +#include "mozilla/ipc/CrashReporterHost.h" + +namespace mozilla { +namespace gfx { + +GPUChild::GPUChild(GPUProcessHost* aHost) + : mHost(aHost), + mGPUReady(false) +{ + MOZ_COUNT_CTOR(GPUChild); +} + +GPUChild::~GPUChild() +{ + MOZ_COUNT_DTOR(GPUChild); +} + +void +GPUChild::Init() +{ + // Build a list of prefs the GPU process will need. Note that because we + // limit the GPU process to prefs contained in gfxPrefs, we can simplify + // the message in two ways: one, we only need to send its index in gfxPrefs + // rather than its name, and two, we only need to send prefs that don't + // have their default value. + nsTArray<GfxPrefSetting> prefs; + for (auto pref : gfxPrefs::all()) { + if (pref->HasDefaultValue()) { + continue; + } + + GfxPrefValue value; + pref->GetCachedValue(&value); + prefs.AppendElement(GfxPrefSetting(pref->Index(), value)); + } + + nsTArray<GfxVarUpdate> updates = gfxVars::FetchNonDefaultVars(); + + DevicePrefs devicePrefs; + devicePrefs.hwCompositing() = gfxConfig::GetValue(Feature::HW_COMPOSITING); + devicePrefs.d3d11Compositing() = gfxConfig::GetValue(Feature::D3D11_COMPOSITING); + devicePrefs.d3d9Compositing() = gfxConfig::GetValue(Feature::D3D9_COMPOSITING); + devicePrefs.oglCompositing() = gfxConfig::GetValue(Feature::OPENGL_COMPOSITING); + devicePrefs.useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D); + + SendInit(prefs, updates, devicePrefs); + + gfxVars::AddReceiver(this); +} + +void +GPUChild::OnVarChanged(const GfxVarUpdate& aVar) +{ + SendUpdateVar(aVar); +} + +void +GPUChild::EnsureGPUReady() +{ + if (mGPUReady) { + return; + } + + GPUDeviceData data; + SendGetDeviceStatus(&data); + + gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS, mHost->GetLaunchTime()); + mGPUReady = true; +} + +bool +GPUChild::RecvInitComplete(const GPUDeviceData& aData) +{ + // We synchronously requested GPU parameters before this arrived. + if (mGPUReady) { + return true; + } + + gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData); + Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS, mHost->GetLaunchTime()); + mGPUReady = true; + return true; +} + +bool +GPUChild::RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) +{ + layers::CheckerboardEventStorage::Report(aSeverity, std::string(aLog.get())); + return true; +} + +bool +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 true; +} + +bool +GPUChild::RecvInitCrashReporter(Shmem&& aShmem) +{ +#ifdef MOZ_CRASHREPORTER + mCrashReporter = MakeUnique<ipc::CrashReporterHost>(GeckoProcessType_GPU, aShmem); +#endif + return true; +} + +bool +GPUChild::RecvNotifyUiObservers(const nsCString& aTopic) +{ + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return true; +} + +bool +GPUChild::RecvAccumulateChildHistogram(InfallibleTArray<Accumulation>&& aAccumulations) +{ + Telemetry::AccumulateChild(GeckoProcessType_GPU, aAccumulations); + return true; +} + +bool +GPUChild::RecvAccumulateChildKeyedHistogram(InfallibleTArray<KeyedAccumulation>&& aAccumulations) +{ + Telemetry::AccumulateChildKeyed(GeckoProcessType_GPU, aAccumulations); + return true; +} + +bool +GPUChild::RecvNotifyDeviceReset() +{ + mHost->mListener->OnProcessDeviceReset(mHost); + return true; +} + +void +GPUChild::ActorDestroy(ActorDestroyReason aWhy) +{ + if (aWhy == AbnormalShutdown) { +#ifdef MOZ_CRASHREPORTER + if (mCrashReporter) { + mCrashReporter->GenerateCrashReport(OtherPid()); + mCrashReporter = nullptr; + } +#endif + Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, + nsDependentCString(XRE_ChildProcessTypeToString(GeckoProcessType_GPU), 1)); + } + + gfxVars::RemoveReceiver(this); + mHost->OnChannelClosed(); +} + +class DeferredDeleteGPUChild : public Runnable +{ +public: + explicit DeferredDeleteGPUChild(UniquePtr<GPUChild>&& aChild) + : mChild(Move(aChild)) + { + } + + NS_IMETHODIMP Run() override { + return NS_OK; + } + +private: + UniquePtr<GPUChild> mChild; +}; + +/* static */ void +GPUChild::Destroy(UniquePtr<GPUChild>&& aChild) +{ + NS_DispatchToMainThread(new DeferredDeleteGPUChild(Move(aChild))); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUChild.h b/gfx/ipc/GPUChild.h new file mode 100644 index 000000000..c0f7d076f --- /dev/null +++ b/gfx/ipc/GPUChild.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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/gfx/PGPUChild.h" +#include "mozilla/gfx/gfxVarReceiver.h" + +namespace mozilla { +namespace ipc { +class CrashReporterHost; +} // namespace +namespace gfx { + +class GPUProcessHost; + +class GPUChild final + : public PGPUChild, + public gfxVarReceiver +{ +public: + explicit GPUChild(GPUProcessHost* aHost); + ~GPUChild(); + + void Init(); + + void EnsureGPUReady(); + + // gfxVarReceiver overrides. + void OnVarChanged(const GfxVarUpdate& aVar) override; + + // PGPUChild overrides. + bool RecvInitComplete(const GPUDeviceData& aData) override; + bool RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override; + bool RecvInitCrashReporter(Shmem&& shmem) override; + bool RecvAccumulateChildHistogram(InfallibleTArray<Accumulation>&& aAccumulations) override; + bool RecvAccumulateChildKeyedHistogram(InfallibleTArray<KeyedAccumulation>&& aAccumulations) override; + void ActorDestroy(ActorDestroyReason aWhy) override; + bool RecvGraphicsError(const nsCString& aError) override; + bool RecvNotifyUiObservers(const nsCString& aTopic) override; + bool RecvNotifyDeviceReset() override; + + static void Destroy(UniquePtr<GPUChild>&& aChild); + +private: + GPUProcessHost* mHost; + UniquePtr<ipc::CrashReporterHost> mCrashReporter; + 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 000000000..d63e17e2f --- /dev/null +++ b/gfx/ipc/GPUParent.cpp @@ -0,0 +1,390 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" +#endif +#include "GPUParent.h" +#include "gfxConfig.h" +#include "gfxPlatform.h" +#include "gfxPrefs.h" +#include "GPUProcessHost.h" +#include "mozilla/Assertions.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/ipc/CrashReporterClient.h" +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/APZCTreeManager.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/dom/VideoDecoderManagerParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/dom/VideoDecoderManagerChild.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "nsDebugImpl.h" +#include "nsExceptionHandler.h" +#include "nsThreadManager.h" +#include "prenv.h" +#include "ProcessUtils.h" +#include "VRManager.h" +#include "VRManagerParent.h" +#include "VsyncBridgeParent.h" +#if defined(XP_WIN) +# include "DeviceManagerD3D9.h" +# include "mozilla/gfx/DeviceManagerDx.h" +#endif +#ifdef MOZ_WIDGET_GTK +# include <gtk/gtk.h> +#endif + +namespace mozilla { +namespace gfx { + +using namespace ipc; +using namespace layers; + +static GPUParent* sGPUParent; + +GPUParent::GPUParent() +{ + sGPUParent = this; +} + +GPUParent::~GPUParent() +{ + sGPUParent = nullptr; +} + +/* static */ GPUParent* +GPUParent::GetSingleton() +{ + return sGPUParent; +} + +bool +GPUParent::Init(base::ProcessId aParentPid, + MessageLoop* aIOLoop, + 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(aChannel, aParentPid, aIOLoop))) { + return false; + } + + nsDebugImpl::SetMultiprocessMode("GPU"); + +#ifdef MOZ_CRASHREPORTER + // Init crash reporter support. + CrashReporterClient::InitSingleton(this); +#endif + + // Ensure gfxPrefs are initialized. + gfxPrefs::GetSingleton(); + gfxConfig::Init(); + gfxVars::Initialize(); + gfxPlatform::InitNullMetadata(); + // Ensure our Factory is initialised, mainly for gfx logging to work. + gfxPlatform::InitMoz2DLogging(); +#if defined(XP_WIN) + DeviceManagerDx::Init(); + DeviceManagerD3D9::Init(); +#endif + + if (NS_FAILED(NS_InitMinimalXPCOM())) { + return false; + } + + CompositorThreadHolder::Start(); + APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop()); + APZCTreeManager::InitializeGlobalState(); + VRManager::ManagerInit(); + LayerTreeOwnerTracker::Initialize(); + mozilla::ipc::SetThisProcessName("GPU Process"); +#ifdef XP_WIN + wmf::MFStartup(); +#endif + return true; +} + +void +GPUParent::NotifyDeviceReset() +{ + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(NS_NewRunnableFunction([] () -> 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 + Unused << SendNotifyDeviceReset(); +} + +bool +GPUParent::RecvInit(nsTArray<GfxPrefSetting>&& prefs, + nsTArray<GfxVarUpdate>&& vars, + const DevicePrefs& devicePrefs) +{ + const nsTArray<gfxPrefs::Pref*>& globalPrefs = gfxPrefs::all(); + for (auto& setting : prefs) { + gfxPrefs::Pref* pref = globalPrefs[setting.index()]; + pref->SetCachedValue(setting.value()); + } + 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::D3D9_COMPOSITING, devicePrefs.d3d9Compositing()); + gfxConfig::Inherit(Feature::OPENGL_COMPOSITING, devicePrefs.oglCompositing()); + gfxConfig::Inherit(Feature::DIRECT2D, devicePrefs.useD2D1()); + +#if defined(XP_WIN) + if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) { + DeviceManagerDx::Get()->CreateCompositorDevices(); + } +#endif + +#if defined(MOZ_WIDGET_GTK) + char* 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); + } +#endif + + // Send a message to the UI process that we're done. + GPUDeviceData data; + RecvGetDeviceStatus(&data); + Unused << SendInitComplete(data); + + return true; +} + +bool +GPUParent::RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) +{ + mVsyncBridge = VsyncBridgeParent::Start(Move(aVsyncEndpoint)); + return true; +} + +bool +GPUParent::RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) +{ + ImageBridgeParent::CreateForGPUProcess(Move(aEndpoint)); + return true; +} + +bool +GPUParent::RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) +{ + VRManagerParent::CreateForGPUProcess(Move(aEndpoint)); + return true; +} + +bool +GPUParent::RecvUpdatePref(const GfxPrefSetting& setting) +{ + gfxPrefs::Pref* pref = gfxPrefs::all()[setting.index()]; + pref->SetCachedValue(setting.value()); + return true; +} + +bool +GPUParent::RecvUpdateVar(const GfxVarUpdate& aUpdate) +{ + gfxVars::ApplyUpdate(aUpdate); + return true; +} + +static void +CopyFeatureChange(Feature aFeature, FeatureChange* 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 = null_t(); + return; + } + + MOZ_ASSERT(!feature.IsEnabled()); + + nsCString message; + message.AssignASCII(feature.GetFailureMessage()); + + *aOut = FeatureFailure(feature.GetValue(), message, feature.GetFailureId()); +} + +bool +GPUParent::RecvGetDeviceStatus(GPUDeviceData* aOut) +{ + CopyFeatureChange(Feature::D3D11_COMPOSITING, &aOut->d3d11Compositing()); + CopyFeatureChange(Feature::D3D9_COMPOSITING, &aOut->d3d9Compositing()); + CopyFeatureChange(Feature::OPENGL_COMPOSITING, &aOut->oglCompositing()); + +#if defined(XP_WIN) + if (DeviceManagerDx* dm = DeviceManagerDx::Get()) { + D3D11DeviceStatus deviceStatus; + dm->ExportDeviceInfo(&deviceStatus); + aOut->gpuDevice() = deviceStatus; + } +#else + aOut->gpuDevice() = null_t(); +#endif + + return true; +} + +static void +OpenParent(RefPtr<CompositorBridgeParent> aParent, + Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + if (!aParent->Bind(Move(aEndpoint))) { + MOZ_CRASH("Failed to bind compositor"); + } +} + +bool +GPUParent::RecvNewWidgetCompositor(Endpoint<layers::PCompositorBridgeParent>&& aEndpoint, + const CSSToLayoutDeviceScale& aScale, + const TimeDuration& aVsyncRate, + const bool& aUseExternalSurfaceSize, + const IntSize& aSurfaceSize) +{ + RefPtr<CompositorBridgeParent> cbp = + new CompositorBridgeParent(aScale, aVsyncRate, aUseExternalSurfaceSize, aSurfaceSize); + + MessageLoop* loop = CompositorThreadHolder::Loop(); + loop->PostTask(NewRunnableFunction(OpenParent, cbp, Move(aEndpoint))); + return true; +} + +bool +GPUParent::RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) +{ + return CompositorBridgeParent::CreateForContent(Move(aEndpoint)); +} + +bool +GPUParent::RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) +{ + return ImageBridgeParent::CreateForContent(Move(aEndpoint)); +} + +bool +GPUParent::RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) +{ + return VRManagerParent::CreateForContent(Move(aEndpoint)); +} + +bool +GPUParent::RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) +{ + return dom::VideoDecoderManagerParent::CreateForContent(Move(aEndpoint)); +} + +bool +GPUParent::RecvAddLayerTreeIdMapping(nsTArray<LayerTreeIdMapping>&& aMappings) +{ + for (const LayerTreeIdMapping& map : aMappings) { + LayerTreeOwnerTracker::Get()->Map(map.layersId(), map.ownerId()); + } + return true; +} + +bool +GPUParent::RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) +{ + LayerTreeOwnerTracker::Get()->Unmap(aMapping.layersId(), aMapping.ownerId()); + CompositorBridgeParent::DeallocateLayerTreeId(aMapping.layersId()); + return true; +} + +bool +GPUParent::RecvNotifyGpuObservers(const nsCString& aTopic) +{ + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + MOZ_ASSERT(obsSvc); + if (obsSvc) { + obsSvc->NotifyObservers(nullptr, aTopic.get(), nullptr); + } + return true; +} + +void +GPUParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (AbnormalShutdown == aWhy) { + NS_WARNING("Shutting down GPU process early due to a crash!"); + ProcessChild::QuickExit(); + } + +#ifdef XP_WIN + wmf::MFShutdown(); +#endif + +#ifndef NS_FREE_PERMANENT_DATA + // No point in going through XPCOM shutdown because we don't keep persistent + // state. + ProcessChild::QuickExit(); +#endif + + if (mVsyncBridge) { + mVsyncBridge->Shutdown(); + mVsyncBridge = nullptr; + } + dom::VideoDecoderManagerParent::ShutdownVideoBridge(); + CompositorThreadHolder::Shutdown(); + Factory::ShutDown(); +#if defined(XP_WIN) + DeviceManagerDx::Shutdown(); + DeviceManagerD3D9::Shutdown(); +#endif + LayerTreeOwnerTracker::Shutdown(); + gfxVars::Shutdown(); + gfxConfig::Shutdown(); + gfxPrefs::DestroySingleton(); +#ifdef MOZ_CRASHREPORTER + CrashReporterClient::DestroySingleton(); +#endif + XRE_ShutdownChildProcess(); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUParent.h b/gfx/ipc/GPUParent.h new file mode 100644 index 000000000..126efce50 --- /dev/null +++ b/gfx/ipc/GPUParent.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +namespace mozilla { +namespace gfx { + +class VsyncBridgeParent; + +class GPUParent final : public PGPUParent +{ +public: + GPUParent(); + ~GPUParent(); + + static GPUParent* GetSingleton(); + + bool Init(base::ProcessId aParentPid, + MessageLoop* aIOLoop, + IPC::Channel* aChannel); + void NotifyDeviceReset(); + + bool RecvInit(nsTArray<GfxPrefSetting>&& prefs, + nsTArray<GfxVarUpdate>&& vars, + const DevicePrefs& devicePrefs) override; + bool RecvInitVsyncBridge(Endpoint<PVsyncBridgeParent>&& aVsyncEndpoint) override; + bool RecvInitImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override; + bool RecvInitVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override; + bool RecvUpdatePref(const GfxPrefSetting& pref) override; + bool RecvUpdateVar(const GfxVarUpdate& pref) override; + bool RecvNewWidgetCompositor( + Endpoint<PCompositorBridgeParent>&& aEndpoint, + const CSSToLayoutDeviceScale& aScale, + const TimeDuration& aVsyncRate, + const bool& aUseExternalSurface, + const IntSize& aSurfaceSize) override; + bool RecvNewContentCompositorBridge(Endpoint<PCompositorBridgeParent>&& aEndpoint) override; + bool RecvNewContentImageBridge(Endpoint<PImageBridgeParent>&& aEndpoint) override; + bool RecvNewContentVRManager(Endpoint<PVRManagerParent>&& aEndpoint) override; + bool RecvNewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent>&& aEndpoint) override; + bool RecvGetDeviceStatus(GPUDeviceData* aOutStatus) override; + bool RecvAddLayerTreeIdMapping(nsTArray<LayerTreeIdMapping>&& aMappings) override; + bool RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override; + bool RecvNotifyGpuObservers(const nsCString& aTopic) override; + + void ActorDestroy(ActorDestroyReason aWhy) override; + +private: + RefPtr<VsyncBridgeParent> mVsyncBridge; +}; + +} // 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 000000000..613f353a4 --- /dev/null +++ b/gfx/ipc/GPUProcessHost.cpp @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sts=8 sw=2 ts=2 tw=99 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/. */ + +#include "GPUProcessHost.h" +#include "chrome/common/process_watcher.h" +#include "gfxPrefs.h" +#include "mozilla/gfx/Logging.h" +#include "nsITimer.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() +{ + MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched); + MOZ_ASSERT(!mGPUChild); + + mLaunchPhase = LaunchPhase::Waiting; + mLaunchTime = TimeStamp::Now(); + + if (!GeckoChildProcessHost::AsyncLaunch()) { + mLaunchPhase = LaunchPhase::Complete; + return false; + } + return true; +} + +bool +GPUProcessHost::WaitForLaunch() +{ + if (mLaunchPhase == LaunchPhase::Complete) { + return !!mGPUChild; + } + + int32_t timeoutMs = gfxPrefs::GPUProcessDevTimeoutMs(); + + // 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; + + if (aSucceeded) { + mProcessToken = ++sProcessTokenCounter; + mGPUChild = MakeUnique<GPUChild>(this); + DebugOnly<bool> rv = + mGPUChild->Open(GetChannel(), 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; + +#ifdef NS_FREE_PERMANENT_DATA + // The channel might already be closed if we got here unexpectedly. + if (!mChannelClosed) { + mGPUChild->Close(); + } +#else + // 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() +{ + if (!mShutdownRequested) { + // This is an unclean shutdown. Notify our listener that we're going away. + mChannelClosed = true; + if (mListener) { + mListener->OnProcessUnexpectedShutdown(this); + } + } + + // Release the actor. + GPUChild::Destroy(Move(mGPUChild)); + MOZ_ASSERT(!mGPUChild); + + // If the owner of GPUProcessHost already requested shutdown, we can now + // schedule destruction. Otherwise we must wait for someone to call + // Shutdown. Note that GPUProcessManager calls Shutdown within + // OnProcessUnexpectedShutdown. + if (mShutdownRequested) { + DestroyProcess(); + } +} + +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; +} + +static void +DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) +{ + XRE_GetIOMessageLoop()-> + PostTask(mozilla::MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess)); +} + +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(); + } + + MessageLoop::current()-> + PostTask(NewRunnableFunction(DelayedDeleteSubprocess, this)); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessHost.h b/gfx/ipc/GPUProcessHost.h new file mode 100644 index 000000000..d5c19f35d --- /dev/null +++ b/gfx/ipc/GPUProcessHost.h @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sts=8 sw=2 ts=2 tw=99 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/. */ + +#ifndef _include_mozilla_gfx_ipc_GPUProcessHost_h_ +#define _include_mozilla_gfx_ipc_GPUProcessHost_h_ + +#include "mozilla/Function.h" +#include "mozilla/Maybe.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/ipc/TaskFactory.h" + +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 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 OnProcessDeviceReset(GPUProcessHost* aHost) + {} + }; + +public: + explicit GPUProcessHost(Listener* listener); + ~GPUProcessHost(); + + // Launch the subprocess asynchronously. On failure, false is returned. + // Otherwise, true is returned, and the OnLaunchComplete 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. + bool Launch(); + + // 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: + // 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(); + +private: + DISALLOW_COPY_AND_ASSIGN(GPUProcessHost); + + Listener* mListener; + ipc::TaskFactory<GPUProcessHost> mTaskFactory; + + enum class LaunchPhase { + Unlaunched, + Waiting, + Complete + }; + LaunchPhase mLaunchPhase; + + UniquePtr<GPUChild> mGPUChild; + uint64_t mProcessToken; + + 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 000000000..38d13484b --- /dev/null +++ b/gfx/ipc/GPUProcessImpl.cpp @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +namespace mozilla { +namespace gfx { + +using namespace ipc; + +GPUProcessImpl::GPUProcessImpl(ProcessId aParentPid) + : ProcessChild(aParentPid) +{ +} + +GPUProcessImpl::~GPUProcessImpl() +{ +} + +bool +GPUProcessImpl::Init() +{ + return mGPU.Init(ParentPid(), + IOThreadChild::message_loop(), + IOThreadChild::channel()); +} + +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 000000000..5e8b6694a --- /dev/null +++ b/gfx/ipc/GPUProcessImpl.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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/MainThreadRuntime.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); + ~GPUProcessImpl(); + + bool Init() override; + void CleanUp() override; + +private: + DISALLOW_COPY_AND_ASSIGN(GPUProcessImpl); + + GPUParent mGPU; + +#if defined(XP_WIN) + // This object initializes and configures COM. + mozilla::mscom::MainThreadRuntime 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 000000000..d69e912de --- /dev/null +++ b/gfx/ipc/GPUProcessListener.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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() + {} + + // Called when the compositor has died and the rendering stack must be + // recreated. + virtual void OnCompositorUnexpectedShutdown() + {} +}; + +} // 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 000000000..0b55cd9b7 --- /dev/null +++ b/gfx/ipc/GPUProcessManager.cpp @@ -0,0 +1,852 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "GPUProcessHost.h" +#include "GPUProcessListener.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/layers/APZCTreeManager.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/CompositorBridgeParent.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" +#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 "mozilla/dom/VideoDecoderManagerChild.h" +#include "mozilla/dom/VideoDecoderManagerParent.h" +#include "MediaPrefs.h" + +namespace mozilla { +namespace gfx { + +using namespace mozilla::layers; + +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), + mNextLayerTreeId(0), + mNumProcessAttempts(0), + mDeviceResetCount(0), + mProcess(nullptr), + mGPUChild(nullptr) +{ + MOZ_COUNT_CTOR(GPUProcessManager); + + mObserver = new Observer(this); + nsContentUtils::RegisterShutdownObserver(mObserver); + + mDeviceResetLastTime = TimeStamp::Now(); + + LayerTreeOwnerTracker::Initialize(); +} + +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(); + } + return NS_OK; +} + +void +GPUProcessManager::OnXPCOMShutdown() +{ + if (mObserver) { + nsContentUtils::UnregisterShutdownObserver(mObserver); + mObserver = nullptr; + } + + CleanShutdown(); +} + +void +GPUProcessManager::LaunchGPUProcess() +{ + if (mProcess) { + return; + } + + // Start the Vsync I/O thread so can use it as soon as the process launches. + EnsureVsyncIOThread(); + + mNumProcessAttempts++; + + // The subprocess is launched asynchronously, so we wait for a callback to + // acquire the IPDL actor. + mProcess = new GPUProcessHost(this); + if (!mProcess->Launch()) { + DisableGPUProcess("Failed to launch GPU process"); + } +} + +void +GPUProcessManager::DisableGPUProcess(const char* aMessage) +{ + if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) { + return; + } + + gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage); + gfxCriticalNote << aMessage; + + DestroyProcess(); + ShutdownVsyncIOThread(); +} + +void +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; + } + } + + if (mGPUChild) { + mGPUChild->EnsureGPUReady(); + } +} + +void +GPUProcessManager::EnsureImageBridgeChild() +{ + if (ImageBridgeChild::GetSingleton()) { + return; + } + + EnsureGPUReady(); + + if (!mGPUChild) { + ImageBridgeChild::InitSameProcess(); + 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(Move(parentPipe)); + ImageBridgeChild::InitWithGPUProcess(Move(childPipe)); +} + +void +GPUProcessManager::EnsureVRManager() +{ + if (VRManagerChild::IsCreated()) { + return; + } + + EnsureGPUReady(); + + if (!mGPUChild) { + 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(Move(parentPipe)); + VRManagerChild::InitWithGPUProcess(Move(childPipe)); +} + +void +GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost) +{ + MOZ_ASSERT(mProcess && mProcess == aHost); + + if (!mProcess->IsConnected()) { + DisableGPUProcess("Failed to launch GPU process"); + return; + } + + mGPUChild = mProcess->GetActor(); + mProcessToken = mProcess->GetProcessToken(); + + Endpoint<PVsyncBridgeParent> vsyncParent; + 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, Move(vsyncChild)); + mGPUChild->SendInitVsyncBridge(Move(vsyncParent)); + + nsTArray<LayerTreeIdMapping> mappings; + LayerTreeOwnerTracker::Get()->Iterate([&](uint64_t aLayersId, base::ProcessId aProcessId) { + mappings.AppendElement(LayerTreeIdMapping(aLayersId, aProcessId)); + }); + mGPUChild->SendAddLayerTreeIdMapping(mappings); +} + +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 = gfxPrefs::DeviceResetThresholdMilliseconds(); + int32_t countLimit = gfxPrefs::DeviceResetLimitCount(); + + bool hasTimeLimit = timeLimit != -1; + bool hasCountLimit = countLimit != -1; + + 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::OnProcessDeviceReset(GPUProcessHost* aHost) +{ + // 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; + + if (ShouldLimitDeviceResets(mDeviceResetCount, delta)) { + DestroyProcess(); + DisableGPUProcess("GPU processed experienced too many device resets"); + + HandleProcessLost(); + return; + } + + // We're good, do a reset like normal + for (auto& session : mRemoteSessions) { + session->NotifyDeviceReset(); + } +} + +void +GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost) +{ + MOZ_ASSERT(mProcess && mProcess == aHost); + + DestroyProcess(); + + if (mNumProcessAttempts > uint32_t(gfxPrefs::GPUProcessDevMaxRestarts())) { + DisableGPUProcess("GPU processed crashed too many times"); + } + + 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 TabChildren + // 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] TabChild::ReinitRendering + // (c) [CONTENT] TabChild::SendEnsureLayersConnected + // (d) [UI] TabParent::RecvEnsureLayersConnected + // (e) [UI] RenderFrameParent::EnsureLayersConnected + // (f) [UI] CompositorBridgeChild::SendNotifyChildRecreated + // + // Note that at step (e), RenderFrameParent 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. + + // 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(); + } + + // 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::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; + if (mVsyncBridge) { + mVsyncBridge->Close(); + mVsyncBridge = nullptr; + } +} + +RefPtr<CompositorSession> +GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget, + LayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize) +{ + uint64_t layerTreeId = AllocateLayerTreeId(); + + EnsureGPUReady(); + EnsureImageBridgeChild(); + EnsureVRManager(); + + if (mGPUChild) { + RefPtr<CompositorSession> session = CreateRemoteSession( + aWidget, + aLayerManager, + layerTreeId, + aScale, + aUseAPZ, + aUseExternalSurfaceSize, + aSurfaceSize); + if (session) { + return session; + } + + // We couldn't create a remote compositor, so abort the process. + DisableGPUProcess("Failed to create remote compositor"); + } + + return InProcessCompositorSession::Create( + aWidget, + aLayerManager, + layerTreeId, + aScale, + aUseAPZ, + aUseExternalSurfaceSize, + aSurfaceSize); +} + +RefPtr<CompositorSession> +GPUProcessManager::CreateRemoteSession(nsBaseWidget* aWidget, + LayerManager* aLayerManager, + const uint64_t& aRootLayerTreeId, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize) +{ +#ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING + ipc::Endpoint<PCompositorBridgeParent> parentPipe; + ipc::Endpoint<PCompositorBridgeChild> childPipe; + + nsresult rv = PCompositorBridge::CreateEndpoints( + mGPUChild->OtherPid(), + base::GetCurrentProcId(), + &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Failed to create PCompositorBridge endpoints: " << hexa(int(rv)); + return nullptr; + } + + RefPtr<CompositorBridgeChild> child = CompositorBridgeChild::CreateRemote( + mProcessToken, + aLayerManager, + Move(childPipe)); + if (!child) { + gfxCriticalNote << "Failed to create CompositorBridgeChild"; + return nullptr; + } + + CompositorWidgetInitData initData; + aWidget->GetCompositorWidgetInitData(&initData); + + TimeDuration vsyncRate = + gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate(); + + bool ok = mGPUChild->SendNewWidgetCompositor( + Move(parentPipe), + aScale, + vsyncRate, + aUseExternalSurfaceSize, + aSurfaceSize); + if (!ok) { + return nullptr; + } + + RefPtr<CompositorVsyncDispatcher> dispatcher = aWidget->GetCompositorVsyncDispatcher(); + RefPtr<CompositorWidgetVsyncObserver> observer = + new CompositorWidgetVsyncObserver(mVsyncBridge, aRootLayerTreeId); + + CompositorWidgetChild* widget = new CompositorWidgetChild(dispatcher, observer); + if (!child->SendPCompositorWidgetConstructor(widget, initData)) { + return nullptr; + } + if (!child->SendInitialize(aRootLayerTreeId)) { + return nullptr; + } + + RefPtr<APZCTreeManagerChild> apz = nullptr; + if (aUseAPZ) { + PAPZCTreeManagerChild* papz = child->SendPAPZCTreeManagerConstructor(0); + if (!papz) { + return nullptr; + } + apz = static_cast<APZCTreeManagerChild*>(papz); + } + + RefPtr<RemoteCompositorSession> session = + new RemoteCompositorSession(aWidget, child, widget, apz, aRootLayerTreeId); + return session.forget(); +#else + gfxCriticalNote << "Platform does not support out-of-process compositing"; + return nullptr; +#endif +} + +bool +GPUProcessManager::CreateContentBridges(base::ProcessId aOtherProcess, + ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor, + ipc::Endpoint<PImageBridgeChild>* aOutImageBridge, + ipc::Endpoint<PVRManagerChild>* aOutVRBridge, + ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager) +{ + if (!CreateContentCompositorBridge(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. + CreateContentVideoDecoderManager(aOtherProcess, aOutVideoManager); + return true; +} + +bool +GPUProcessManager::CreateContentCompositorBridge(base::ProcessId aOtherProcess, + ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint) +{ + EnsureGPUReady(); + + ipc::Endpoint<PCompositorBridgeParent> parentPipe; + ipc::Endpoint<PCompositorBridgeChild> childPipe; + + base::ProcessId gpuPid = mGPUChild + ? mGPUChild->OtherPid() + : base::GetCurrentProcId(); + + nsresult rv = PCompositorBridge::CreateEndpoints( + gpuPid, + aOtherProcess, + &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor bridge: " << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentCompositorBridge(Move(parentPipe)); + } else { + if (!CompositorBridgeParent::CreateForContent(Move(parentPipe))) { + return false; + } + } + + *aOutEndpoint = Move(childPipe); + return true; +} + +bool +GPUProcessManager::CreateContentImageBridge(base::ProcessId aOtherProcess, + ipc::Endpoint<PImageBridgeChild>* aOutEndpoint) +{ + EnsureImageBridgeChild(); + + base::ProcessId gpuPid = mGPUChild + ? mGPUChild->OtherPid() + : base::GetCurrentProcId(); + + ipc::Endpoint<PImageBridgeParent> parentPipe; + ipc::Endpoint<PImageBridgeChild> childPipe; + nsresult rv = PImageBridge::CreateEndpoints( + gpuPid, + aOtherProcess, + &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor bridge: " << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentImageBridge(Move(parentPipe)); + } else { + if (!ImageBridgeParent::CreateForContent(Move(parentPipe))) { + return false; + } + } + + *aOutEndpoint = 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 gpuPid = mGPUChild + ? mGPUChild->OtherPid() + : base::GetCurrentProcId(); + + ipc::Endpoint<PVRManagerParent> parentPipe; + ipc::Endpoint<PVRManagerChild> childPipe; + nsresult rv = PVRManager::CreateEndpoints( + gpuPid, + aOtherProcess, + &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content compositor bridge: " << hexa(int(rv)); + return false; + } + + if (mGPUChild) { + mGPUChild->SendNewContentVRManager(Move(parentPipe)); + } else { + if (!VRManagerParent::CreateForContent(Move(parentPipe))) { + return false; + } + } + + *aOutEndpoint = Move(childPipe); + return true; +} + +void +GPUProcessManager::CreateContentVideoDecoderManager(base::ProcessId aOtherProcess, + ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndpoint) +{ + if (!mGPUChild || !MediaPrefs::PDMUseGPUDecoder()) { + return; + } + + ipc::Endpoint<dom::PVideoDecoderManagerParent> parentPipe; + ipc::Endpoint<dom::PVideoDecoderManagerChild> childPipe; + + nsresult rv = dom::PVideoDecoderManager::CreateEndpoints( + mGPUChild->OtherPid(), + aOtherProcess, + &parentPipe, + &childPipe); + if (NS_FAILED(rv)) { + gfxCriticalNote << "Could not create content video decoder: " << hexa(int(rv)); + return; + } + + mGPUChild->SendNewContentVideoDecoderManager(Move(parentPipe)); + + *aOutEndpoint = Move(childPipe); + return; +} + +already_AddRefed<IAPZCTreeManager> +GPUProcessManager::GetAPZCTreeManagerForLayers(uint64_t aLayersId) +{ + return CompositorBridgeParent::GetAPZCTreeManager(aLayersId); +} + +void +GPUProcessManager::MapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId) +{ + LayerTreeOwnerTracker::Get()->Map(aLayersId, aOwningId); + + if (mGPUChild) { + AutoTArray<LayerTreeIdMapping, 1> mappings; + mappings.AppendElement(LayerTreeIdMapping(aLayersId, aOwningId)); + mGPUChild->SendAddLayerTreeIdMapping(mappings); + } +} + +void +GPUProcessManager::UnmapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId) +{ + LayerTreeOwnerTracker::Get()->Unmap(aLayersId, aOwningId); + + if (mGPUChild) { + mGPUChild->SendRemoveLayerTreeIdMapping(LayerTreeIdMapping(aLayersId, aOwningId)); + return; + } + CompositorBridgeParent::DeallocateLayerTreeId(aLayersId); +} + +bool +GPUProcessManager::IsLayerTreeIdMapped(uint64_t aLayersId, base::ProcessId aRequestingId) +{ + return LayerTreeOwnerTracker::Get()->IsMapped(aLayersId, aRequestingId); +} + +uint64_t +GPUProcessManager::AllocateLayerTreeId() +{ + MOZ_ASSERT(NS_IsMainThread()); + return ++mNextLayerTreeId; +} + +void +GPUProcessManager::EnsureVsyncIOThread() +{ + if (mVsyncIOThread) { + return; + } + + mVsyncIOThread = new VsyncIOThreadHolder(); + MOZ_RELEASE_ASSERT(mVsyncIOThread->Start()); +} + +void +GPUProcessManager::ShutdownVsyncIOThread() +{ + mVsyncIOThread = nullptr; +} + +void +GPUProcessManager::RegisterSession(RemoteCompositorSession* aSession) +{ + mRemoteSessions.AppendElement(aSession); +} + +void +GPUProcessManager::UnregisterSession(RemoteCompositorSession* aSession) +{ + mRemoteSessions.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 (!mGPUChild) { + return false; + } + nsCString topic(aTopic); + mGPUChild->SendNotifyGpuObservers(topic); + return true; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/GPUProcessManager.h b/gfx/ipc/GPUProcessManager.h new file mode 100644 index 000000000..84ed03609 --- /dev/null +++ b/gfx/ipc/GPUProcessManager.h @@ -0,0 +1,234 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "nsIObserverService.h" +#include "nsThreadUtils.h" +class nsBaseWidget; + + +namespace mozilla { +namespace layers { +class IAPZCTreeManager; +class CompositorSession; +class CompositorUpdateObserver; +class PCompositorBridgeChild; +class PImageBridgeChild; +class RemoteCompositorSession; +} // namespace layers +namespace widget { +class CompositorWidget; +} // namespace widget +namespace dom { +class ContentParent; +class TabParent; +class PVideoDecoderManagerChild; +} // 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; + + typedef layers::CompositorSession CompositorSession; + typedef layers::CompositorUpdateObserver CompositorUpdateObserver; + typedef layers::IAPZCTreeManager IAPZCTreeManager; + typedef layers::LayerManager LayerManager; + typedef layers::PCompositorBridgeChild PCompositorBridgeChild; + typedef layers::PImageBridgeChild PImageBridgeChild; + typedef layers::RemoteCompositorSession RemoteCompositorSession; + +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(); + + // 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. + void EnsureGPUReady(); + + RefPtr<CompositorSession> CreateTopLevelCompositor( + nsBaseWidget* aWidget, + LayerManager* aLayerManager, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize); + + bool CreateContentBridges( + base::ProcessId aOtherProcess, + ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor, + ipc::Endpoint<PImageBridgeChild>* aOutImageBridge, + ipc::Endpoint<PVRManagerChild>* aOutVRBridge, + ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager); + + // This returns a reference to the APZCTreeManager to which + // pan/zoom-related events can be sent. + already_AddRefed<IAPZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId); + + // Maps the layer tree and process together so that aOwningPID is allowed + // to access aLayersId across process. + void MapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId); + + // Release compositor-thread resources referred to by |aID|. + // + // Must run on the content main thread. + void UnmapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId); + + // Checks to see if aLayersId and aRequestingPID have been mapped by MapLayerTreeId + bool IsLayerTreeIdMapped(uint64_t 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 content main thread. + uint64_t AllocateLayerTreeId(); + + + void OnProcessLaunchComplete(GPUProcessHost* aHost) override; + void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override; + void OnProcessDeviceReset(GPUProcessHost* aHost) override; + + // 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(); + + // 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; + } + +private: + // Called from our xpcom-shutdown observer. + void OnXPCOMShutdown(); + + bool CreateContentCompositorBridge(base::ProcessId aOtherProcess, + ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint); + bool CreateContentImageBridge(base::ProcessId aOtherProcess, + ipc::Endpoint<PImageBridgeChild>* aOutEndpoint); + bool CreateContentVRManager(base::ProcessId aOtherProcess, + ipc::Endpoint<PVRManagerChild>* aOutEndpoint); + void CreateContentVideoDecoderManager(base::ProcessId aOtherProcess, + ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint); + + // Called from RemoteCompositorSession. We track remote sessions so we can + // notify their owning widgets that the session must be restarted. + void RegisterSession(RemoteCompositorSession* aSession); + void UnregisterSession(RemoteCompositorSession* aSession); + +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 EnsureImageBridgeChild(); + void EnsureVRManager(); + + RefPtr<CompositorSession> CreateRemoteSession( + nsBaseWidget* aWidget, + LayerManager* aLayerManager, + const uint64_t& aRootLayerTreeId, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + 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: + ~Observer() {} + + GPUProcessManager* mManager; + }; + friend class Observer; + +private: + RefPtr<Observer> mObserver; + ipc::TaskFactory<GPUProcessManager> mTaskFactory; + RefPtr<VsyncIOThreadHolder> mVsyncIOThread; + uint64_t mNextLayerTreeId; + uint32_t mNumProcessAttempts; + + nsTArray<RefPtr<RemoteCompositorSession>> mRemoteSessions; + nsTArray<GPUProcessListener*> mListeners; + + uint32_t mDeviceResetCount; + TimeStamp mDeviceResetLastTime; + + // Fields that are associated with the current GPU process. + GPUProcessHost* mProcess; + MOZ_INIT_OUTSIDE_CTOR uint64_t mProcessToken; + GPUChild* mGPUChild; + RefPtr<VsyncBridgeChild> mVsyncBridge; +}; + +} // 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 000000000..e45aa7040 --- /dev/null +++ b/gfx/ipc/GfxMessageUtils.h @@ -0,0 +1,1273 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et 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 "FrameMetrics.h" +#include "ImageTypes.h" +#include "RegionBuilder.h" +#include "base/process_util.h" +#include "chrome/common/ipc_message_utils.h" +#include "gfxPoint.h" +#include "gfxRect.h" +#include "gfxTelemetry.h" +#include "gfxTypes.h" +#include "ipc/IPCMessageUtils.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/layers/AsyncDragMetrics.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/GeckoContentController.h" +#include "mozilla/layers/LayersTypes.h" +#include "nsRect.h" +#include "nsRegion.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) + { + return ReadParam(aMsg, aIter, &aResult->x) && + ReadParam(aMsg, aIter, &aResult->y) && + ReadParam(aMsg, aIter, &aResult->width) && + ReadParam(aMsg, aIter, &aResult->height); + } +}; + +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::layers::LayersBackend> + : public ContiguousEnumSerializer< + mozilla::layers::LayersBackend, + mozilla::layers::LayersBackend::LAYERS_NONE, + mozilla::layers::LayersBackend::LAYERS_LAST> +{}; + +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::FeatureStatus> + : public ContiguousEnumSerializer< + mozilla::gfx::FeatureStatus, + mozilla::gfx::FeatureStatus::Unused, + mozilla::gfx::FeatureStatus::LAST> +{}; + +template <> +struct ParamTraits<mozilla::layers::ScaleMode> + : public ContiguousEnumSerializer< + mozilla::layers::ScaleMode, + mozilla::layers::ScaleMode::SCALE_NONE, + mozilla::layers::ScaleMode::SENTINEL> +{}; + +template <> +struct ParamTraits<mozilla::gfx::AttributeName> + : public ContiguousEnumSerializer< + mozilla::gfx::AttributeName, + mozilla::gfx::eBlendBlendmode, + mozilla::gfx::eLastAttributeName> +{}; + +template <> +struct ParamTraits<mozilla::gfx::AttributeType> + : public ContiguousEnumSerializer< + mozilla::gfx::AttributeType, + mozilla::gfx::AttributeType::eBool, + mozilla::gfx::AttributeType::Max> +{}; + +template <> +struct ParamTraits<mozilla::gfx::PrimitiveType> + : public ContiguousEnumSerializer< + mozilla::gfx::PrimitiveType, + mozilla::gfx::PrimitiveType::Empty, + mozilla::gfx::PrimitiveType::Max> +{}; + +template <> +struct ParamTraits<mozilla::gfx::ColorSpace> + : public ContiguousEnumSerializer< + mozilla::gfx::ColorSpace, + mozilla::gfx::ColorSpace::SRGB, + mozilla::gfx::ColorSpace::Max> +{}; + +template <> +struct ParamTraits<mozilla::layers::TextureFlags> + : public BitFlagsEnumSerializer< + mozilla::layers::TextureFlags, + mozilla::layers::TextureFlags::ALL_BITS> +{}; + +template <> +struct ParamTraits<mozilla::layers::DiagnosticTypes> + : public BitFlagsEnumSerializer< + mozilla::layers::DiagnosticTypes, + mozilla::layers::DiagnosticTypes::ALL_BITS> +{}; + +/* +template <> +struct ParamTraits<mozilla::PixelFormat> + : public EnumSerializer<mozilla::PixelFormat, + SurfaceFormat::A8R8G8B8_UINT32, + SurfaceFormat::UNKNOWN> +{}; +*/ + +template<> +struct ParamTraits<mozilla::gfx::Color> +{ + typedef mozilla::gfx::Color 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) + { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y) && + ReadParam(msg, iter, &result->width) && + ReadParam(msg, iter, &result->height)); + } +}; + +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) + { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y) && + ReadParam(msg, iter, &result->width) && + ReadParam(msg, iter, &result->height)); + } +}; + +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<> +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) + { + return (ReadParam(msg, iter, &result->x) && + ReadParam(msg, iter, &result->y) && + ReadParam(msg, iter, &result->width) && + ReadParam(msg, iter, &result->height)); + } +}; + +template<> +struct ParamTraits<nsRegion> + : RegionParamTraits<nsRegion, nsRect, nsRegion::RectIterator> +{}; + +template<> +struct ParamTraits<mozilla::layers::FrameMetrics::ScrollOffsetUpdateType> + : public ContiguousEnumSerializer< + mozilla::layers::FrameMetrics::ScrollOffsetUpdateType, + mozilla::layers::FrameMetrics::ScrollOffsetUpdateType::eNone, + mozilla::layers::FrameMetrics::ScrollOffsetUpdateType::eSentinel> +{}; + +// Helper class for reading bitfields. +// If T has bitfields members, derive ParamTraits<T> from BitfieldHelper<T>. +template <typename ParamType> +struct BitfieldHelper +{ + // We need this helper because we can't get the address of a bitfield to + // pass directly to ReadParam. So instead we read it into a temporary bool + // and set the bitfield using a setter function + static bool ReadBoolForBitfield(const Message* aMsg, PickleIterator* aIter, + ParamType* aResult, void (ParamType::*aSetter)(bool)) + { + bool value; + if (ReadParam(aMsg, aIter, &value)) { + (aResult->*aSetter)(value); + return true; + } + return false; + } +}; + +template <> +struct ParamTraits<mozilla::layers::FrameMetrics> + : BitfieldHelper<mozilla::layers::FrameMetrics> +{ + typedef mozilla::layers::FrameMetrics paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mScrollId); + WriteParam(aMsg, aParam.mPresShellResolution); + WriteParam(aMsg, aParam.mCompositionBounds); + WriteParam(aMsg, aParam.mDisplayPort); + WriteParam(aMsg, aParam.mCriticalDisplayPort); + WriteParam(aMsg, aParam.mScrollableRect); + WriteParam(aMsg, aParam.mCumulativeResolution); + WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel); + WriteParam(aMsg, aParam.mScrollOffset); + WriteParam(aMsg, aParam.mZoom); + WriteParam(aMsg, aParam.mScrollGeneration); + WriteParam(aMsg, aParam.mSmoothScrollOffset); + WriteParam(aMsg, aParam.mRootCompositionSize); + WriteParam(aMsg, aParam.mDisplayPortMargins); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mViewport); + WriteParam(aMsg, aParam.mExtraResolution); + WriteParam(aMsg, aParam.mPaintRequestTime); + WriteParam(aMsg, aParam.mScrollUpdateType); + WriteParam(aMsg, aParam.mIsRootContent); + WriteParam(aMsg, aParam.mDoSmoothScroll); + WriteParam(aMsg, aParam.mUseDisplayPortMargins); + WriteParam(aMsg, aParam.mIsScrollInfoLayer); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mScrollId) && + ReadParam(aMsg, aIter, &aResult->mPresShellResolution) && + ReadParam(aMsg, aIter, &aResult->mCompositionBounds) && + ReadParam(aMsg, aIter, &aResult->mDisplayPort) && + ReadParam(aMsg, aIter, &aResult->mCriticalDisplayPort) && + ReadParam(aMsg, aIter, &aResult->mScrollableRect) && + ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) && + ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) && + ReadParam(aMsg, aIter, &aResult->mScrollOffset) && + ReadParam(aMsg, aIter, &aResult->mZoom) && + ReadParam(aMsg, aIter, &aResult->mScrollGeneration) && + ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) && + ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) && + ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mViewport) && + ReadParam(aMsg, aIter, &aResult->mExtraResolution) && + ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) && + ReadParam(aMsg, aIter, &aResult->mScrollUpdateType) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetIsRootContent) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetDoSmoothScroll) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetUseDisplayPortMargins) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetIsScrollInfoLayer)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollSnapInfo> +{ + typedef mozilla::layers::ScrollSnapInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mScrollSnapTypeX); + WriteParam(aMsg, aParam.mScrollSnapTypeY); + WriteParam(aMsg, aParam.mScrollSnapIntervalX); + WriteParam(aMsg, aParam.mScrollSnapIntervalY); + WriteParam(aMsg, aParam.mScrollSnapDestination); + WriteParam(aMsg, aParam.mScrollSnapCoordinates); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mScrollSnapTypeX) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapTypeY) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapIntervalX) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapIntervalY) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapDestination) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapCoordinates)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::LayerClip> +{ + typedef mozilla::layers::LayerClip paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mClipRect); + WriteParam(aMsg, aParam.mMaskLayerIndex); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mClipRect) && + ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::ScrollMetadata> + : BitfieldHelper<mozilla::layers::ScrollMetadata> +{ + typedef mozilla::layers::ScrollMetadata paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mMetrics); + WriteParam(aMsg, aParam.mSnapInfo); + WriteParam(aMsg, aParam.mScrollParentId); + WriteParam(aMsg, aParam.mBackgroundColor); + WriteParam(aMsg, aParam.GetContentDescription()); + WriteParam(aMsg, aParam.mLineScrollAmount); + WriteParam(aMsg, aParam.mPageScrollAmount); + WriteParam(aMsg, aParam.mScrollClip); + WriteParam(aMsg, aParam.mHasScrollgrab); + WriteParam(aMsg, aParam.mAllowVerticalScrollWithWheel); + WriteParam(aMsg, aParam.mIsLayersIdRoot); + WriteParam(aMsg, aParam.mUsesContainerScrolling); + WriteParam(aMsg, aParam.mForceDisableApz); + } + + static bool ReadContentDescription(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + nsCString str; + if (!ReadParam(aMsg, aIter, &str)) { + return false; + } + aResult->SetContentDescription(str); + return true; + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mMetrics) && + ReadParam(aMsg, aIter, &aResult->mSnapInfo) && + ReadParam(aMsg, aIter, &aResult->mScrollParentId) && + ReadParam(aMsg, aIter, &aResult->mBackgroundColor) && + ReadContentDescription(aMsg, aIter, aResult) && + ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) && + ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) && + ReadParam(aMsg, aIter, &aResult->mScrollClip) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetHasScrollgrab) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetAllowVerticalScrollWithWheel) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetIsLayersIdRoot) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetUsesContainerScrolling) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetForceDisableApz)); + } +}; + +template<> +struct ParamTraits<GeckoProcessType> + : public ContiguousEnumSerializer< + GeckoProcessType, + GeckoProcessType_Default, + GeckoProcessType_End> +{}; + +template<> +struct ParamTraits<mozilla::layers::TextureFactoryIdentifier> +{ + typedef mozilla::layers::TextureFactoryIdentifier paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mParentBackend); + WriteParam(aMsg, aParam.mParentProcessType); + WriteParam(aMsg, aParam.mMaxTextureSize); + WriteParam(aMsg, aParam.mSupportsTextureBlitting); + WriteParam(aMsg, aParam.mSupportsPartialUploads); + WriteParam(aMsg, aParam.mSupportsComponentAlpha); + WriteParam(aMsg, aParam.mSyncHandle); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + bool result = ReadParam(aMsg, aIter, &aResult->mParentBackend) && + ReadParam(aMsg, aIter, &aResult->mParentProcessType) && + ReadParam(aMsg, aIter, &aResult->mMaxTextureSize) && + ReadParam(aMsg, aIter, &aResult->mSupportsTextureBlitting) && + ReadParam(aMsg, aIter, &aResult->mSupportsPartialUploads) && + ReadParam(aMsg, aIter, &aResult->mSupportsComponentAlpha) && + ReadParam(aMsg, aIter, &aResult->mSyncHandle); + return result; + } +}; + +template<> +struct ParamTraits<mozilla::layers::TextureInfo> +{ + typedef mozilla::layers::TextureInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mCompositableType); + WriteParam(aMsg, aParam.mTextureFlags); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mCompositableType) && + ReadParam(aMsg, aIter, &aResult->mTextureFlags); + } +}; + +template <> +struct ParamTraits<mozilla::layers::CompositableType> + : public ContiguousEnumSerializer< + mozilla::layers::CompositableType, + mozilla::layers::CompositableType::UNKNOWN, + mozilla::layers::CompositableType::COUNT> +{}; + +template <> +struct ParamTraits<mozilla::gfx::SurfaceFormat> + : public ContiguousEnumSerializer< + mozilla::gfx::SurfaceFormat, + mozilla::gfx::SurfaceFormat::B8G8R8A8, + mozilla::gfx::SurfaceFormat::UNKNOWN> +{}; + +template <> +struct ParamTraits<mozilla::StereoMode> + : public ContiguousEnumSerializer< + mozilla::StereoMode, + mozilla::StereoMode::MONO, + mozilla::StereoMode::MAX> +{}; + +template <> +struct ParamTraits<mozilla::YUVColorSpace> + : public ContiguousEnumSerializer< + mozilla::YUVColorSpace, + mozilla::YUVColorSpace::BT601, + mozilla::YUVColorSpace::UNKNOWN> +{}; + +template <> +struct ParamTraits<mozilla::layers::ScrollableLayerGuid> +{ + typedef mozilla::layers::ScrollableLayerGuid paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mLayersId); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mScrollId); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mLayersId) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mScrollId)); + } +}; + + +template <> +struct ParamTraits<mozilla::layers::ZoomConstraints> +{ + typedef mozilla::layers::ZoomConstraints paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mAllowZoom); + WriteParam(aMsg, aParam.mAllowDoubleTapZoom); + WriteParam(aMsg, aParam.mMinZoom); + WriteParam(aMsg, aParam.mMaxZoom); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mAllowZoom) && + ReadParam(aMsg, aIter, &aResult->mAllowDoubleTapZoom) && + ReadParam(aMsg, aIter, &aResult->mMinZoom) && + ReadParam(aMsg, aIter, &aResult->mMaxZoom)); + } +}; + +template <> +struct ParamTraits<mozilla::layers::EventRegions> +{ + typedef mozilla::layers::EventRegions paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mHitRegion); + WriteParam(aMsg, aParam.mDispatchToContentHitRegion); + WriteParam(aMsg, aParam.mNoActionRegion); + WriteParam(aMsg, aParam.mHorizontalPanRegion); + WriteParam(aMsg, aParam.mVerticalPanRegion); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mHitRegion) && + ReadParam(aMsg, aIter, &aResult->mDispatchToContentHitRegion) && + ReadParam(aMsg, aIter, &aResult->mNoActionRegion) && + ReadParam(aMsg, aIter, &aResult->mHorizontalPanRegion) && + ReadParam(aMsg, aIter, &aResult->mVerticalPanRegion)); + } +}; + +template <> +struct ParamTraits<mozilla::gfx::AttributeMap> +{ + typedef mozilla::gfx::AttributeMap paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.Count()); + for (auto iter = aParam.ConstIter(); !iter.Done(); iter.Next()) { + mozilla::gfx::AttributeName name = + mozilla::gfx::AttributeName(iter.Key()); + mozilla::gfx::AttributeType type = + mozilla::gfx::AttributeMap::GetType(iter.UserData()); + + WriteParam(aMsg, type); + WriteParam(aMsg, name); + + switch (type) { + +#define CASE_TYPE(typeName) \ + case mozilla::gfx::AttributeType::e##typeName: \ + WriteParam(aMsg, aParam.Get##typeName(name)); \ + break; + + CASE_TYPE(Bool) + CASE_TYPE(Uint) + CASE_TYPE(Float) + CASE_TYPE(Size) + CASE_TYPE(IntSize) + CASE_TYPE(IntPoint) + CASE_TYPE(Matrix) + CASE_TYPE(Matrix5x4) + CASE_TYPE(Point3D) + CASE_TYPE(Color) + CASE_TYPE(AttributeMap) + CASE_TYPE(Floats) + +#undef CASE_TYPE + + default: + MOZ_CRASH("GFX: unhandled attribute type"); + } + } + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + uint32_t count; + if (!ReadParam(aMsg, aIter, &count)) { + return false; + } + for (uint32_t i = 0; i < count; i++) { + mozilla::gfx::AttributeType type; + if (!ReadParam(aMsg, aIter, &type)) { + return false; + } + mozilla::gfx::AttributeName name; + if (!ReadParam(aMsg, aIter, &name)) { + return false; + } + switch (type) { + +#define HANDLE_TYPE(type, typeName) \ + case mozilla::gfx::AttributeType::e##typeName: \ + { \ + type value; \ + if (!ReadParam(aMsg, aIter, &value)) { \ + return false; \ + } \ + aResult->Set(name, value); \ + break; \ + } + + HANDLE_TYPE(bool, Bool) + HANDLE_TYPE(uint32_t, Uint) + HANDLE_TYPE(float, Float) + HANDLE_TYPE(mozilla::gfx::Size, Size) + HANDLE_TYPE(mozilla::gfx::IntSize, IntSize) + HANDLE_TYPE(mozilla::gfx::IntPoint, IntPoint) + HANDLE_TYPE(mozilla::gfx::Matrix, Matrix) + HANDLE_TYPE(mozilla::gfx::Matrix5x4, Matrix5x4) + HANDLE_TYPE(mozilla::gfx::Point3D, Point3D) + HANDLE_TYPE(mozilla::gfx::Color, Color) + HANDLE_TYPE(mozilla::gfx::AttributeMap, AttributeMap) + +#undef HANDLE_TYPE + + case mozilla::gfx::AttributeType::eFloats: + { + nsTArray<float> value; + if (!ReadParam(aMsg, aIter, &value)) { + return false; + } + aResult->Set(name, &value[0], value.Length()); + break; + } + default: + MOZ_CRASH("GFX: unhandled attribute type"); + } + } + return true; + } +}; + +template <> +struct ParamTraits<mozilla::gfx::FilterPrimitiveDescription> +{ + typedef mozilla::gfx::FilterPrimitiveDescription paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.Type()); + WriteParam(aMsg, aParam.PrimitiveSubregion()); + WriteParam(aMsg, aParam.FilterSpaceBounds()); + WriteParam(aMsg, aParam.IsTainted()); + WriteParam(aMsg, aParam.OutputColorSpace()); + WriteParam(aMsg, aParam.NumberOfInputs()); + for (size_t i = 0; i < aParam.NumberOfInputs(); i++) { + WriteParam(aMsg, aParam.InputPrimitiveIndex(i)); + WriteParam(aMsg, aParam.InputColorSpace(i)); + } + WriteParam(aMsg, aParam.Attributes()); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + mozilla::gfx::PrimitiveType type; + mozilla::gfx::IntRect primitiveSubregion; + mozilla::gfx::IntRect filterSpaceBounds; + bool isTainted = false; + mozilla::gfx::ColorSpace outputColorSpace; + size_t numberOfInputs = 0; + if (!ReadParam(aMsg, aIter, &type) || + !ReadParam(aMsg, aIter, &primitiveSubregion) || + !ReadParam(aMsg, aIter, &filterSpaceBounds) || + !ReadParam(aMsg, aIter, &isTainted) || + !ReadParam(aMsg, aIter, &outputColorSpace) || + !ReadParam(aMsg, aIter, &numberOfInputs)) { + return false; + } + + aResult->SetType(type); + aResult->SetPrimitiveSubregion(primitiveSubregion); + aResult->SetFilterSpaceBounds(filterSpaceBounds); + aResult->SetIsTainted(isTainted); + aResult->SetOutputColorSpace(outputColorSpace); + + for (size_t i = 0; i < numberOfInputs; i++) { + int32_t inputPrimitiveIndex = 0; + mozilla::gfx::ColorSpace inputColorSpace; + if (!ReadParam(aMsg, aIter, &inputPrimitiveIndex) || + !ReadParam(aMsg, aIter, &inputColorSpace)) { + return false; + } + aResult->SetInputPrimitive(i, inputPrimitiveIndex); + aResult->SetInputColorSpace(i, inputColorSpace); + } + + return ReadParam(aMsg, aIter, &aResult->Attributes()); + } +}; + +template <> +struct ParamTraits<mozilla::gfx::FilterDescription> +{ + typedef mozilla::gfx::FilterDescription paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mPrimitives); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mPrimitives)); + } +}; + +typedef mozilla::layers::GeckoContentController::TapType TapType; + +template <> +struct ParamTraits<TapType> + : public ContiguousEnumSerializer< + TapType, + TapType::eSingleTap, + TapType::eSentinel> +{}; + +typedef mozilla::layers::GeckoContentController::APZStateChange APZStateChange; + +template <> +struct ParamTraits<APZStateChange> + : public ContiguousEnumSerializer< + APZStateChange, + APZStateChange::eTransformBegin, + APZStateChange::eSentinel> +{}; + +template<> +struct ParamTraits<mozilla::layers::EventRegionsOverride> + : public BitFlagsEnumSerializer< + mozilla::layers::EventRegionsOverride, + mozilla::layers::EventRegionsOverride::ALL_BITS> +{}; + +template<> +struct ParamTraits<mozilla::layers::AsyncDragMetrics::DragDirection> + : public ContiguousEnumSerializer< + mozilla::layers::AsyncDragMetrics::DragDirection, + mozilla::layers::AsyncDragMetrics::DragDirection::NONE, + mozilla::layers::AsyncDragMetrics::DragDirection::SENTINEL> +{}; + +template<> +struct ParamTraits<mozilla::layers::AsyncDragMetrics> +{ + typedef mozilla::layers::AsyncDragMetrics paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mViewId); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mDragStartSequenceNumber); + WriteParam(aMsg, aParam.mScrollbarDragOffset); + WriteParam(aMsg, aParam.mScrollTrack); + WriteParam(aMsg, aParam.mDirection); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mViewId) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mDragStartSequenceNumber) && + ReadParam(aMsg, aIter, &aResult->mScrollbarDragOffset) && + ReadParam(aMsg, aIter, &aResult->mScrollTrack) && + ReadParam(aMsg, aIter, &aResult->mDirection)); + } +}; + +} /* namespace IPC */ + +#endif /* __GFXMESSAGEUTILS_H__ */ diff --git a/gfx/ipc/GraphicsMessages.ipdlh b/gfx/ipc/GraphicsMessages.ipdlh new file mode 100644 index 000000000..cc4115d42 --- /dev/null +++ b/gfx/ipc/GraphicsMessages.ipdlh @@ -0,0 +1,87 @@ +/* -*- 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 "ipc/IPCMessageUtils.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"; + +namespace mozilla { +namespace gfx { + +struct D3D11DeviceStatus +{ + bool isWARP; + bool textureSharingWorks; + uint32_t featureLevel; + DxgiAdapterDesc adapter; +}; + +struct DevicePrefs +{ + FeatureStatus hwCompositing; + FeatureStatus d3d11Compositing; + FeatureStatus d3d9Compositing; + FeatureStatus oglCompositing; + FeatureStatus useD2D1; +}; + +struct ContentDeviceData +{ + DevicePrefs prefs; + D3D11DeviceStatus d3d11; +}; + +// Represents the state of a feature that has failed to initialize. +struct FeatureFailure +{ + FeatureStatus status; + nsCString message; + nsCString failureId; +}; + +// If a feature state has changed from Enabled -> Failure, this will be non- +// null. +union FeatureChange +{ + null_t; + FeatureFailure; +}; + +union GPUDeviceStatus +{ + null_t; + D3D11DeviceStatus; +}; + +struct GPUDeviceData +{ + FeatureChange d3d11Compositing; + FeatureChange d3d9Compositing; + FeatureChange oglCompositing; + GPUDeviceStatus gpuDevice; +}; + +union GfxVarValue +{ + BackendType; + bool; + gfxImageFormat; + IntSize; + nsCString; +}; + +struct GfxVarUpdate +{ + size_t index; + GfxVarValue value; +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/InProcessCompositorSession.cpp b/gfx/ipc/InProcessCompositorSession.cpp new file mode 100644 index 000000000..fd8525085 --- /dev/null +++ b/gfx/ipc/InProcessCompositorSession.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +// so we can cast an APZCTreeManager to an IAPZCTreeManager +#include "mozilla/layers/APZCTreeManager.h" +#include "mozilla/layers/IAPZCTreeManager.h" + +namespace mozilla { +namespace layers { + +InProcessCompositorSession::InProcessCompositorSession(widget::CompositorWidget* aWidget, + CompositorBridgeChild* aChild, + CompositorBridgeParent* aParent) + : CompositorSession(aWidget->AsDelegate(), aChild, aParent->RootLayerTreeId()), + mCompositorBridgeParent(aParent), + mCompositorWidget(aWidget) +{ +} + +/* static */ RefPtr<InProcessCompositorSession> +InProcessCompositorSession::Create(nsIWidget* aWidget, + LayerManager* aLayerManager, + const uint64_t& aRootLayerTreeId, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize) +{ + CompositorWidgetInitData initData; + aWidget->GetCompositorWidgetInitData(&initData); + + RefPtr<CompositorWidget> widget = CompositorWidget::CreateLocal(initData, aWidget); + RefPtr<CompositorBridgeChild> child = new CompositorBridgeChild(aLayerManager); + RefPtr<CompositorBridgeParent> parent = + child->InitSameProcess(widget, aRootLayerTreeId, aScale, aUseAPZ, aUseExternalSurfaceSize, aSurfaceSize); + + return new InProcessCompositorSession(widget, child, parent); +} + +CompositorBridgeParent* +InProcessCompositorSession::GetInProcessBridge() const +{ + return mCompositorBridgeParent; +} + +void +InProcessCompositorSession::SetContentController(GeckoContentController* aController) +{ + mCompositorBridgeParent->SetControllerForLayerTree(mRootLayerTreeId, aController); +} + +RefPtr<IAPZCTreeManager> +InProcessCompositorSession::GetAPZCTreeManager() const +{ + return mCompositorBridgeParent->GetAPZCTreeManager(mRootLayerTreeId); +} + +bool +InProcessCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) +{ + return mCompositorBridgeParent->ResetCompositor(aBackendHints, aOutIdentifier); +} + +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. + mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; + mCompositorBridgeParent = nullptr; + mCompositorWidget = nullptr; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/InProcessCompositorSession.h b/gfx/ipc/InProcessCompositorSession.h new file mode 100644 index 000000000..deb642da2 --- /dev/null +++ b/gfx/ipc/InProcessCompositorSession.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +namespace mozilla { +namespace layers { + +// A CompositorSession where both the child and parent CompositorBridge reside +// in the same process. +class InProcessCompositorSession final : public CompositorSession +{ +public: + static RefPtr<InProcessCompositorSession> Create( + nsIWidget* aWidget, + LayerManager* aLayerManager, + const uint64_t& aRootLayerTreeId, + CSSToLayoutDeviceScale aScale, + bool aUseAPZ, + bool aUseExternalSurfaceSize, + const gfx::IntSize& aSurfaceSize); + + CompositorBridgeParent* GetInProcessBridge() const override; + void SetContentController(GeckoContentController* aController) override; + RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override; + bool Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) override; + void Shutdown() override; + +private: + InProcessCompositorSession(widget::CompositorWidget* aWidget, + 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 000000000..c442335c9 --- /dev/null +++ b/gfx/ipc/PGPU.ipdl @@ -0,0 +1,106 @@ +/* -*- 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 protocol PCompositorBridge; +include protocol PImageBridge; +include protocol PVRManager; +include protocol PVsyncBridge; +include protocol PVideoDecoderManager; + +using base::ProcessId from "base/process.h"; +using mozilla::TimeDuration from "mozilla/TimeStamp.h"; +using mozilla::CSSToLayoutDeviceScale from "Units.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/2D.h"; +using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h"; + +namespace mozilla { +namespace gfx { + +union GfxPrefValue { + bool; + int32_t; + uint32_t; + float; +}; + +struct GfxPrefSetting { + int32_t index; + GfxPrefValue value; +}; + +struct LayerTreeIdMapping { + uint64_t layersId; + ProcessId ownerId; +}; + +sync protocol PGPU +{ +parent: + // Sent by the UI process to initiate core settings. + async Init(GfxPrefSetting[] prefs, + GfxVarUpdate[] vars, + DevicePrefs devicePrefs); + + async InitVsyncBridge(Endpoint<PVsyncBridgeParent> endpoint); + async InitImageBridge(Endpoint<PImageBridgeParent> endpoint); + async InitVRManager(Endpoint<PVRManagerParent> endpoint); + + // Called to update a gfx preference or variable. + async UpdatePref(GfxPrefSetting pref); + async UpdateVar(GfxVarUpdate var); + + // Create a new top-level compositor. + async NewWidgetCompositor(Endpoint<PCompositorBridgeParent> endpoint, + CSSToLayoutDeviceScale scale, + TimeDuration vsyncRate, + bool useExternalSurface, + IntSize surfaceSize); + + // Create a new content-process compositor bridge. + async NewContentCompositorBridge(Endpoint<PCompositorBridgeParent> endpoint); + async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint); + async NewContentVRManager(Endpoint<PVRManagerParent> endpoint); + async NewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent> 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); + + // Have a message be broadcasted to the GPU process by the GPU process + // observer service. + async NotifyGpuObservers(nsCString aTopic); + +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(Shmem shmem); + + // 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 AccumulateChildHistogram(Accumulation[] accumulations); + async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations); + + async NotifyDeviceReset(); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/PVsyncBridge.ipdl b/gfx/ipc/PVsyncBridge.ipdl new file mode 100644 index 000000000..03c68bbde --- /dev/null +++ b/gfx/ipc/PVsyncBridge.ipdl @@ -0,0 +1,22 @@ +/* -*- 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"; + +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(TimeStamp vsyncTimeStamp, uint64_t layersId); +}; + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/RemoteCompositorSession.cpp b/gfx/ipc/RemoteCompositorSession.cpp new file mode 100644 index 000000000..6e5ad975a --- /dev/null +++ b/gfx/ipc/RemoteCompositorSession.cpp @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "mozilla/VsyncDispatcher.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/Unused.h" +#include "nsBaseWidget.h" + +namespace mozilla { +namespace layers { + +using namespace gfx; +using namespace widget; + +RemoteCompositorSession::RemoteCompositorSession(nsBaseWidget* aWidget, + CompositorBridgeChild* aChild, + CompositorWidgetDelegate* aWidgetDelegate, + APZCTreeManagerChild* aAPZ, + const uint64_t& aRootLayerTreeId) + : CompositorSession(aWidgetDelegate, aChild, aRootLayerTreeId), + mWidget(aWidget), + mAPZ(aAPZ) +{ + GPUProcessManager::Get()->RegisterSession(this); + if (mAPZ) { + mAPZ->SetCompositorSession(this); + } +} + +RemoteCompositorSession::~RemoteCompositorSession() +{ + // This should have been shutdown first. + MOZ_ASSERT(!mCompositorBridgeChild); +} + +void +RemoteCompositorSession::NotifyDeviceReset() +{ + MOZ_ASSERT(mWidget); + mWidget->OnRenderingDeviceReset(); +} + +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. + MOZ_ASSERT(mWidget); + mWidget->NotifyRemoteCompositorSessionLost(this); +} + +CompositorBridgeParent* +RemoteCompositorSession::GetInProcessBridge() const +{ + return nullptr; +} + +void +RemoteCompositorSession::SetContentController(GeckoContentController* aController) +{ + mContentController = aController; + mCompositorBridgeChild->SendPAPZConstructor(new APZChild(aController), 0); +} + +GeckoContentController* +RemoteCompositorSession::GetContentController() +{ + return mContentController.get(); +} + +nsIWidget* +RemoteCompositorSession::GetWidget() +{ + return mWidget; +} + +RefPtr<IAPZCTreeManager> +RemoteCompositorSession::GetAPZCTreeManager() const +{ + return mAPZ; +} + +bool +RemoteCompositorSession::Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) +{ + bool didReset; + Unused << mCompositorBridgeChild->SendReset(aBackendHints, &didReset, aOutIdentifier); + return didReset; +} + +void +RemoteCompositorSession::Shutdown() +{ + mContentController = nullptr; + if (mAPZ) { + mAPZ->SetCompositorSession(nullptr); + } + mCompositorBridgeChild->Destroy(); + mCompositorBridgeChild = nullptr; + mCompositorWidgetDelegate = nullptr; + mWidget = nullptr; + GPUProcessManager::Get()->UnregisterSession(this); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/ipc/RemoteCompositorSession.h b/gfx/ipc/RemoteCompositorSession.h new file mode 100644 index 000000000..b4731c5c2 --- /dev/null +++ b/gfx/ipc/RemoteCompositorSession.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +namespace mozilla { +namespace layers { + +class RemoteCompositorSession final : public CompositorSession +{ +public: + RemoteCompositorSession(nsBaseWidget* aWidget, + CompositorBridgeChild* aChild, + CompositorWidgetDelegate* aWidgetDelegate, + APZCTreeManagerChild* aAPZ, + const uint64_t& aRootLayerTreeId); + ~RemoteCompositorSession() override; + + CompositorBridgeParent* GetInProcessBridge() const override; + void SetContentController(GeckoContentController* aController) override; + GeckoContentController* GetContentController(); + nsIWidget* GetWidget(); + RefPtr<IAPZCTreeManager> GetAPZCTreeManager() const override; + bool Reset(const nsTArray<LayersBackend>& aBackendHints, TextureFactoryIdentifier* aOutIdentifier) override; + void Shutdown() override; + + void NotifyDeviceReset(); + void NotifySessionLost(); + +private: + nsBaseWidget* mWidget; + 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 000000000..ec68a61fa --- /dev/null +++ b/gfx/ipc/SharedDIB.cpp @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 2; 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 "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("", false, false, aSize)) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +bool +SharedDIB::IsValid() +{ + if (!mShMem) + return false; + + return mShMem->IsHandleValid(mShMem->handle()); +} + +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 000000000..6c48b2875 --- /dev/null +++ b/gfx/ipc/SharedDIB.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#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 000000000..696bb300c --- /dev/null +++ b/gfx/ipc/SharedDIBSurface.cpp @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; 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 "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 000000000..79f0c3bbe --- /dev/null +++ b/gfx/ipc/SharedDIBSurface.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#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 000000000..197e197d4 --- /dev/null +++ b/gfx/ipc/SharedDIBWin.cpp @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 2; 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 "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->handle(), + kHeaderBytes); + if (!mSharedBmp) + return NS_ERROR_FAILURE; + + mOldObj = SelectObject(mSharedHdc, mSharedBmp); + + return NS_OK; +} + +} // gfx +} // mozilla diff --git a/gfx/ipc/SharedDIBWin.h b/gfx/ipc/SharedDIBWin.h new file mode 100644 index 000000000..06759662b --- /dev/null +++ b/gfx/ipc/SharedDIBWin.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#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); +}; + +} // gfx +} // mozilla + +#endif diff --git a/gfx/ipc/VsyncBridgeChild.cpp b/gfx/ipc/VsyncBridgeChild.cpp new file mode 100644 index 000000000..a4be827ef --- /dev/null +++ b/gfx/ipc/VsyncBridgeChild.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +namespace mozilla { +namespace gfx { + +VsyncBridgeChild::VsyncBridgeChild(RefPtr<VsyncIOThreadHolder> aThread, const uint64_t& aProcessToken) + : mThread(aThread), + mLoop(nullptr), + mProcessToken(aProcessToken) +{ +} + +VsyncBridgeChild::~VsyncBridgeChild() +{ +} + +/* 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>&&>( + child, &VsyncBridgeChild::Open, 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; + } + + mLoop = MessageLoop::current(); + + // Last reference is freed in DeallocPVsyncBridgeChild. + AddRef(); +} + +class NotifyVsyncTask : public Runnable +{ +public: + NotifyVsyncTask(RefPtr<VsyncBridgeChild> aVsyncBridge, + TimeStamp aTimeStamp, + const uint64_t& aLayersId) + : mVsyncBridge(aVsyncBridge), + mTimeStamp(aTimeStamp), + mLayersId(aLayersId) + {} + + NS_IMETHOD Run() override { + mVsyncBridge->NotifyVsyncImpl(mTimeStamp, mLayersId); + return NS_OK; + } + +private: + RefPtr<VsyncBridgeChild> mVsyncBridge; + TimeStamp mTimeStamp; + uint64_t mLayersId; +}; + +bool +VsyncBridgeChild::IsOnVsyncIOThread() const +{ + return MessageLoop::current() == mLoop; +} + +void +VsyncBridgeChild::NotifyVsync(TimeStamp aTimeStamp, const uint64_t& aLayersId) +{ + // This should be on the Vsync thread (not the Vsync I/O thread). + MOZ_ASSERT(!IsOnVsyncIOThread()); + + RefPtr<NotifyVsyncTask> task = new NotifyVsyncTask(this, aTimeStamp, aLayersId); + mLoop->PostTask(task.forget()); +} + +void +VsyncBridgeChild::NotifyVsyncImpl(TimeStamp aTimeStamp, const uint64_t& aLayersId) +{ + // This should be on the Vsync I/O thread. + MOZ_ASSERT(IsOnVsyncIOThread()); + + if (!mProcessToken) { + return; + } + SendNotifyVsync(aTimeStamp, aLayersId); +} + +void +VsyncBridgeChild::Close() +{ + if (!IsOnVsyncIOThread()) { + mLoop->PostTask(NewRunnableMethod(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::DeallocPVsyncBridgeChild() +{ + Release(); +} + +void +VsyncBridgeChild::ProcessingError(Result aCode, const char* aReason) +{ + MOZ_RELEASE_ASSERT(aCode == MsgDropped, "Processing error in VsyncBridgeChild"); +} + +void +VsyncBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const +{ + dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid()); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncBridgeChild.h b/gfx/ipc/VsyncBridgeChild.h new file mode 100644 index 000000000..866138062 --- /dev/null +++ b/gfx/ipc/VsyncBridgeChild.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 DeallocPVsyncBridgeChild() override; + void ProcessingError(Result aCode, const char* aReason) override; + + void NotifyVsync(TimeStamp aTimeStamp, const uint64_t& aLayersId); + + virtual void HandleFatalError(const char* aName, const char* aMsg) const override; + +private: + VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken); + ~VsyncBridgeChild(); + + void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint); + + void NotifyVsyncImpl(TimeStamp aTimeStamp, const uint64_t& aLayersId); + + bool IsOnVsyncIOThread() const; + +private: + RefPtr<VsyncIOThreadHolder> mThread; + MessageLoop* mLoop; + 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 000000000..96ef5de0f --- /dev/null +++ b/gfx/ipc/VsyncBridgeParent.cpp @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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/layers/CompositorThread.h" + +namespace mozilla { +namespace gfx { + +RefPtr<VsyncBridgeParent> +VsyncBridgeParent::Start(Endpoint<PVsyncBridgeParent>&& aEndpoint) +{ + RefPtr<VsyncBridgeParent> parent = new VsyncBridgeParent(); + + RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVsyncBridgeParent>&&>( + parent, &VsyncBridgeParent::Open, Move(aEndpoint)); + CompositorThreadHolder::Loop()->PostTask(task.forget()); + + return parent; +} + +VsyncBridgeParent::VsyncBridgeParent() + : mOpen(false) +{ + MOZ_COUNT_CTOR(VsyncBridgeParent); +} + +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; +} + +bool +VsyncBridgeParent::RecvNotifyVsync(const TimeStamp& aTimeStamp, const uint64_t& aLayersId) +{ + CompositorBridgeParent::NotifyVsync(aTimeStamp, aLayersId); + return true; +} + +void +VsyncBridgeParent::Shutdown() +{ + MessageLoop* ccloop = CompositorThreadHolder::Loop(); + if (MessageLoop::current() != ccloop) { + ccloop->PostTask(NewRunnableMethod(this, &VsyncBridgeParent::ShutdownImpl)); + return; + } + + ShutdownImpl(); +} + +void +VsyncBridgeParent::ShutdownImpl() +{ + if (mOpen) { + Close(); + mOpen = false; + } +} + +void +VsyncBridgeParent::ActorDestroy(ActorDestroyReason aWhy) +{ + mOpen = false; +} + +void +VsyncBridgeParent::DeallocPVsyncBridgeParent() +{ + Release(); +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/ipc/VsyncBridgeParent.h b/gfx/ipc/VsyncBridgeParent.h new file mode 100644 index 000000000..c194843ee --- /dev/null +++ b/gfx/ipc/VsyncBridgeParent.h @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 gfx { + +class VsyncBridgeParent final : public PVsyncBridgeParent +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncBridgeParent) + + static RefPtr<VsyncBridgeParent> Start(Endpoint<PVsyncBridgeParent>&& aEndpoint); + + bool RecvNotifyVsync(const TimeStamp& vsyncTimeStamp, const uint64_t& aLayersId) override; + void ActorDestroy(ActorDestroyReason aWhy) override; + void DeallocPVsyncBridgeParent() override; + + void Shutdown(); + +private: + VsyncBridgeParent(); + ~VsyncBridgeParent(); + + void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint); + void ShutdownImpl(); + +private: + bool mOpen; +}; + +} // 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 000000000..8730b7213 --- /dev/null +++ b/gfx/ipc/VsyncIOThreadHolder.cpp @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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" + +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 { + NS_DispatchToMainThread(NewRunnableMethod(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 000000000..e34f2896a --- /dev/null +++ b/gfx/ipc/VsyncIOThreadHolder.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=99: */ +/* 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 "nsThreadUtils.h" + +namespace mozilla { +namespace gfx { + +class VsyncIOThreadHolder final +{ +public: + VsyncIOThreadHolder(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncIOThreadHolder) + + bool Start(); + + RefPtr<nsIThread> GetThread() const; + +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 000000000..ff3a81228 --- /dev/null +++ b/gfx/ipc/moz.build @@ -0,0 +1,82 @@ +# -*- 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/. + +EXPORTS.mozilla += [ + 'D3DMessageUtils.h', + 'GfxMessageUtils.h' +] + +EXPORTS.mozilla.gfx += [ + 'GPUChild.h', + 'GPUParent.h', + 'GPUProcessHost.h', + 'GPUProcessImpl.h', + 'GPUProcessListener.h', + 'GPUProcessManager.h', + 'SharedDIB.h', + 'VsyncBridgeChild.h', + 'VsyncBridgeParent.h', + 'VsyncIOThreadHolder.h', +] + +EXPORTS.mozilla.layers += [ + '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', + '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', + 'PGPU.ipdl', + 'PVsyncBridge.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'] |