Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Brindusan Cristian 2019-06-11 12:44:12 +03:00
Родитель 7282e461be c606cdd6d0
Коммит d957131916
90 изменённых файлов: 1230 добавлений и 2517 удалений

Просмотреть файл

@ -3516,6 +3516,13 @@ void Document::LocalizationLinkAdded(Element* aLinkElement) {
// container is reached.
mL10nResources.AppendElement(href);
if (!mPendingInitialTranslation) {
// Our initial translation is going to block layout start. Make sure we
// don't fire the load event until after that stops happening and layout
// has a chance to start.
BlockOnload();
}
mPendingInitialTranslation = true;
}
}
@ -3570,6 +3577,13 @@ void Document::TriggerInitialDocumentTranslation() {
}
void Document::InitialDocumentTranslationCompleted() {
if (mPendingInitialTranslation) {
// This means we blocked the load event in LocalizationLinkAdded. It's
// important that the load blocker removal here be async, because our caller
// will notify the content sink after us, and we want the content sync's
// work to happen before the load event fires.
UnblockOnload(/* aFireSync = */ false);
}
mPendingInitialTranslation = false;
}

Просмотреть файл

@ -3861,12 +3861,9 @@ class Document : public nsINode,
* This method is called when the initial translation
* of the document is completed.
*
* It unblocks the layout.
*
* This method is virtual so that XULDocument can
* override it.
* It unblocks the load event if translation was blocking it.
*/
virtual void InitialDocumentTranslationCompleted();
void InitialDocumentTranslationCompleted();
protected:
RefPtr<DocumentL10n> mDocumentL10n;

Просмотреть файл

@ -26,7 +26,7 @@
#include "mozilla/Unused.h"
#include "mozilla/StaticPrefs.h"
#include "mozilla/TelemetryIPC.h"
#include "mozilla/VideoDecoderManagerChild.h"
#include "mozilla/RemoteDecoderManagerChild.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
#include "mozilla/docshell/OfflineCacheUpdateChild.h"
#include "mozilla/dom/BrowsingContext.h"
@ -1215,7 +1215,7 @@ void ContentChild::LaunchRDDProcess() {
Endpoint<PRemoteDecoderManagerChild> endpoint;
Unused << SendLaunchRDDProcess(&rv, &endpoint);
if (rv == NS_OK) {
RemoteDecoderManagerChild::InitForContent(std::move(endpoint));
RemoteDecoderManagerChild::InitForRDDProcess(std::move(endpoint));
}
}));
task.Wait();
@ -1472,7 +1472,7 @@ mozilla::ipc::IPCResult ContentChild::RecvInitRendering(
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces) {
MOZ_ASSERT(namespaces.Length() == 3);
@ -1496,7 +1496,7 @@ mozilla::ipc::IPCResult ContentChild::RecvInitRendering(
if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge))) {
return GetResultForRenderingInitFailure(aVRBridge.OtherPid());
}
VideoDecoderManagerChild::InitForContent(std::move(aVideoManager));
RemoteDecoderManagerChild::InitForGPUProcess(std::move(aVideoManager));
#if defined(XP_MACOSX) && !defined(MOZ_SANDBOX)
// Close all current connections to the WindowServer. This ensures that the
@ -1514,7 +1514,7 @@ mozilla::ipc::IPCResult ContentChild::RecvReinitRendering(
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces) {
MOZ_ASSERT(namespaces.Length() == 3);
nsTArray<RefPtr<BrowserChild>> tabs = BrowserChild::GetAll();
@ -1549,7 +1549,7 @@ mozilla::ipc::IPCResult ContentChild::RecvReinitRendering(
}
}
VideoDecoderManagerChild::InitForContent(std::move(aVideoManager));
RemoteDecoderManagerChild::InitForGPUProcess(std::move(aVideoManager));
return IPC_OK();
}

Просмотреть файл

@ -183,7 +183,7 @@ class ContentChild final : public PContentChild,
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces);
mozilla::ipc::IPCResult RecvRequestPerformanceMetrics(const nsID& aID);
@ -192,7 +192,7 @@ class ContentChild final : public PContentChild,
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces);
mozilla::ipc::IPCResult RecvAudioDefaultDeviceChange();

Просмотреть файл

@ -2516,7 +2516,7 @@ void ContentParent::InitInternal(ProcessPriority aInitialPriority) {
Endpoint<PCompositorManagerChild> compositor;
Endpoint<PImageBridgeChild> imageBridge;
Endpoint<PVRManagerChild> vrBridge;
Endpoint<PVideoDecoderManagerChild> videoManager;
Endpoint<PRemoteDecoderManagerChild> videoManager;
AutoTArray<uint32_t, 3> namespaces;
DebugOnly<bool> opened =
@ -2682,7 +2682,7 @@ void ContentParent::OnCompositorUnexpectedShutdown() {
Endpoint<PCompositorManagerChild> compositor;
Endpoint<PImageBridgeChild> imageBridge;
Endpoint<PVRManagerChild> vrBridge;
Endpoint<PVideoDecoderManagerChild> videoManager;
Endpoint<PRemoteDecoderManagerChild> videoManager;
AutoTArray<uint32_t, 3> namespaces;
DebugOnly<bool> opened =

Просмотреть файл

@ -41,7 +41,6 @@ include protocol PPresentation;
include protocol PURLClassifier;
include protocol PURLClassifierLocal;
include protocol PVRManager;
include protocol PVideoDecoderManager;
include protocol PRemoteDecoderManager;
include protocol PProfiler;
include protocol PScriptCache;
@ -415,7 +414,7 @@ child:
Endpoint<PCompositorManagerChild> compositor,
Endpoint<PImageBridgeChild> imageBridge,
Endpoint<PVRManagerChild> vr,
Endpoint<PVideoDecoderManagerChild> video,
Endpoint<PRemoteDecoderManagerChild> video,
uint32_t[] namespaces);
// Re-create the rendering stack using the given endpoints. This is sent
@ -425,7 +424,7 @@ child:
Endpoint<PCompositorManagerChild> compositor,
Endpoint<PImageBridgeChild> bridge,
Endpoint<PVRManagerChild> vr,
Endpoint<PVideoDecoderManagerChild> video,
Endpoint<PRemoteDecoderManagerChild> video,
uint32_t[] namespaces);
async AudioDefaultDeviceChange();

Просмотреть файл

@ -8,8 +8,8 @@
#include "base/thread.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/StaticPrefs.h"
#include "VideoDecoderChild.h"
#include "VideoDecoderManagerChild.h"
#include "RemoteVideoDecoder.h"
#include "RemoteDecoderManagerChild.h"
#include "RemoteMediaDataDecoder.h"
@ -21,7 +21,7 @@ using namespace layers;
using namespace gfx;
nsresult GpuDecoderModule::Startup() {
if (!VideoDecoderManagerChild::GetManagerThread()) {
if (!RemoteDecoderManagerChild::GetManagerThread()) {
return NS_ERROR_FAILURE;
}
return mWrapped->Startup();
@ -50,10 +50,10 @@ already_AddRefed<MediaDataDecoder> GpuDecoderModule::CreateVideoDecoder(
return mWrapped->CreateVideoDecoder(aParams);
}
RefPtr<VideoDecoderChild> child = new VideoDecoderChild();
RefPtr<GpuRemoteVideoDecoderChild> child = new GpuRemoteVideoDecoderChild();
SynchronousTask task("InitIPDL");
MediaResult result(NS_OK);
VideoDecoderManagerChild::GetManagerThread()->Dispatch(
RemoteDecoderManagerChild::GetManagerThread()->Dispatch(
NS_NewRunnableFunction(
"dom::GpuDecoderModule::CreateVideoDecoder",
[&, child]() {
@ -73,8 +73,8 @@ already_AddRefed<MediaDataDecoder> GpuDecoderModule::CreateVideoDecoder(
}
RefPtr<RemoteMediaDataDecoder> object = new RemoteMediaDataDecoder(
child, VideoDecoderManagerChild::GetManagerThread(),
VideoDecoderManagerChild::GetManagerAbstractThread());
child, RemoteDecoderManagerChild::GetManagerThread(),
RemoteDecoderManagerChild::GetManagerAbstractThread());
return object.forget();
}

Просмотреть файл

@ -6,10 +6,13 @@
include "mozilla/dom/MediaIPCUtils.h";
include protocol PRemoteDecoderManager;
include PMediaDecoderParams;
include LayersSurfaces;
using mozilla::MediaDataDecoder::ConversionRequired from "PlatformDecoderModule.h";
using mozilla::TrackInfo::TrackType from "MediaInfo.h";
using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
include PMediaDecoderParams;
include LayersSurfaces;
namespace mozilla {
@ -18,7 +21,7 @@ struct RemoteVideoDataIPDL
MediaDataIPDL base;
IntSize display;
IntSize frameSize;
SurfaceDescriptorBuffer sdBuffer;
SurfaceDescriptor sd;
int32_t frameID;
};
@ -39,9 +42,10 @@ union DecodedOutputIPDL
// This protocol provides a way to use MediaDataDecoder across processes.
// The parent side currently is only implemented to work with
// RemoteDecoderModule.
// RemoteDecoderModule or WindowsMediaFoundation.
// The child side runs in the content process, and the parent side runs
// in the RDD process. We run a separate IPDL thread for both sides.
// in the RDD process or the GPU process. We run a separate IPDL thread
// for both sides.
async protocol PRemoteDecoder
{
manager PRemoteDecoderManager;
@ -59,14 +63,18 @@ parent:
async __delete__();
child:
async InitComplete(TrackType trackType,
nsCString decoderDescription,
async InitComplete(TrackType trackType, nsCString decoderDescription,
bool hardware, nsCString hardwareReason,
ConversionRequired conversion);
async InitFailed(nsresult reason);
async FlushComplete();
async ShutdownComplete();
// Each output may include a SurfaceDescriptorGPUVideo that represents the decoded
// frame. This SurfaceDescriptor can be used on the Layers IPDL protocol, but
// must be released explicitly using DeallocateSurfaceDescriptorGPUVideo
// on the manager protocol.
async Output(DecodedOutputIPDL data);
async InputExhausted();
async DrainComplete();

Просмотреть файл

@ -3,11 +3,14 @@
* 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 protocol PTexture;
include protocol PRemoteDecoder;
include LayersSurfaces;
include "mozilla/dom/MediaIPCUtils.h";
using VideoInfo from "MediaInfo.h";
using AudioInfo from "MediaInfo.h";
using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
using mozilla::CreateDecoderParams::OptionSet from "PlatformDecoderModule.h";
namespace mozilla {
@ -29,10 +32,23 @@ sync protocol PRemoteDecoderManager
manages PRemoteDecoder;
parent:
// aBlacklistedD3D11Driver and aBlacklistedD3D9Driver are used to read back the blacklisted driver information
// from GPU process to content process.
// We should have added a new sync method to read back this information but, in that way, we also introduce one
// more sync IPC call.
// Considering that this information is only used for telemetry usage in bug 1393392 and should be removed once
// we have collected enough data, we add these two return values here for convenience.
sync PRemoteDecoder(RemoteDecoderInfoIPDL info,
OptionSet options)
OptionSet options,
TextureFactoryIdentifier identifier)
returns (bool success,
nsCString aBlacklistedD3D11Driver,
nsCString aBlacklistedD3D9Driver,
nsCString aErrorDescription);
sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult);
async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd);
};
} // namespace mozilla

Просмотреть файл

@ -1,60 +0,0 @@
/* -*- 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 protocol PVideoDecoderManager;
include PMediaDecoderParams;
include LayersSurfaces;
using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
namespace mozilla {
struct VideoDataIPDL
{
MediaDataIPDL base;
IntSize display;
IntSize frameSize;
SurfaceDescriptorGPUVideo sd;
int32_t frameID;
};
// This protocol provides a way to use MediaDataDecoder across processes.
// The parent side currently is only implemented to work with
// Window Media Foundation, but can be extended easily to support other backends.
// The child side runs in the content process, and the parent side runs in the
// GPU process. We run a separate IPDL thread for both sides.
async protocol PVideoDecoder
{
manager PVideoDecoderManager;
parent:
async Init();
async Input(MediaRawDataIPDL data);
async Flush();
async Drain();
async Shutdown();
// To clear the threshold, call with INT64_MIN.
async SetSeekThreshold(TimeUnit time);
async __delete__();
child:
async InitComplete(nsCString decoderDescription, bool hardware, nsCString hardwareReason, uint32_t conversion);
async InitFailed(nsresult reason);
async FlushComplete();
async ShutdownComplete();
// Each output includes a SurfaceDescriptorGPUVideo that represents the decoded
// frame. This SurfaceDescriptor can be used on the Layers IPDL protocol, but
// must be released explicitly using DeallocateSurfaceDescriptorGPUVideo
// on the manager protocol.
async Output(VideoDataIPDL data);
async InputExhausted();
async DrainComplete();
async Error(nsresult error);
};
} // namespace mozilla

Просмотреть файл

@ -1,38 +0,0 @@
/* -*- 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 protocol PTexture;
include protocol PVideoDecoder;
include LayersSurfaces;
include "mozilla/dom/MediaIPCUtils.h";
using VideoInfo from "MediaInfo.h";
using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
using mozilla::CreateDecoderParams::Option from "PlatformDecoderModule.h";
using mozilla::CreateDecoderParams::OptionSet from "PlatformDecoderModule.h";
namespace mozilla {
sync protocol PVideoDecoderManager
{
manages PVideoDecoder;
parent:
// aBlacklistedD3D11Driver and aBlacklistedD3D9Driver are used to read back the blacklisted driver information
// from GPU process to content process.
// We should have added a new sync method to read back this information but, in that way, we also introduce one
// more sync IPC call.
// Considering that this information is only used for telemetry usage in bug 1393392 and should be removed once
// we have collected enough data, we add these two return values here for convenience.
sync PVideoDecoder(VideoInfo info, float framerate, OptionSet options, TextureFactoryIdentifier identifier)
returns (bool success,
nsCString aBlacklistedD3D11Driver,
nsCString aBlacklistedD3D9Driver,
nsCString aErrorDescription);
sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult);
async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd);
};
} // namespace mozilla

Просмотреть файл

@ -38,7 +38,7 @@ MediaResult RemoteAudioDecoderChild::InitIPDL(
const AudioInfo& aAudioInfo,
const CreateDecoderParams::OptionSet& aOptions) {
RefPtr<RemoteDecoderManagerChild> manager =
RemoteDecoderManagerChild::GetSingleton();
RemoteDecoderManagerChild::GetRDDProcessSingleton();
// The manager isn't available because RemoteDecoderManagerChild has been
// initialized with null end points and we don't want to decode video on RDD
@ -56,8 +56,12 @@ MediaResult RemoteAudioDecoderChild::InitIPDL(
mIPDLSelfRef = this;
bool success = false;
nsCString errorDescription;
if (manager->SendPRemoteDecoderConstructor(this, aAudioInfo, aOptions,
&success, &errorDescription)) {
nsCString blacklistedD3D11Driver;
nsCString blacklistedD3D9Driver;
layers::TextureFactoryIdentifier defaultIdent;
if (manager->SendPRemoteDecoderConstructor(
this, aAudioInfo, aOptions, defaultIdent, &success,
&blacklistedD3D11Driver, &blacklistedD3D9Driver, &errorDescription)) {
mCanSend = true;
}

Просмотреть файл

@ -9,8 +9,9 @@
namespace mozilla {
RemoteDecoderChild::RemoteDecoderChild()
: mThread(RemoteDecoderManagerChild::GetManagerThread()) {}
RemoteDecoderChild::RemoteDecoderChild(bool aRecreatedOnCrash)
: mThread(RemoteDecoderManagerChild::GetManagerThread()),
mRecreatedOnCrash(aRecreatedOnCrash) {}
RemoteDecoderChild::~RemoteDecoderChild() {
AssertOnManagerThread();
@ -44,12 +45,15 @@ mozilla::ipc::IPCResult RemoteDecoderChild::RecvError(const nsresult& aError) {
}
mozilla::ipc::IPCResult RemoteDecoderChild::RecvInitComplete(
const TrackInfo::TrackType& trackType, const nsCString& aDecoderDescription,
const ConversionRequired& aConversion) {
const TrackInfo::TrackType& aTrackType,
const nsCString& aDecoderDescription, const bool& aHardware,
const nsCString& aHardwareReason, const ConversionRequired& aConversion) {
AssertOnManagerThread();
mInitPromise.ResolveIfExists(trackType, __func__);
mInitPromise.ResolveIfExists(aTrackType, __func__);
mInitialized = true;
mDescription = aDecoderDescription;
mIsHardwareAccelerated = aHardware;
mHardwareAcceleratedReason = aHardwareReason;
mConversion = aConversion;
return IPC_OK();
}
@ -80,15 +84,45 @@ void RemoteDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
MOZ_ASSERT(mCanSend);
// If the IPC channel is gone pending promises need to be resolved/rejected.
if (aWhy == AbnormalShutdown) {
MediaResult error(NS_ERROR_DOM_MEDIA_DECODE_ERR);
mDecodePromise.RejectIfExists(error, __func__);
mDrainPromise.RejectIfExists(error, __func__);
mFlushPromise.RejectIfExists(error, __func__);
mShutdownPromise.ResolveIfExists(true, __func__);
RefPtr<RemoteDecoderChild> kungFuDeathGrip = mShutdownSelfRef.forget();
Unused << kungFuDeathGrip;
// GPU process crashed, record the time and send back to MFR for telemetry.
mRemoteProcessCrashTime = TimeStamp::Now();
if (mRecreatedOnCrash) {
// Defer reporting an error until we've recreated the manager so that
// it'll be safe for MediaFormatReader to recreate decoders
RefPtr<RemoteDecoderChild> ref = this;
// Make sure shutdown self reference is null. Since ref is captured by the
// lambda it is not necessary to keep it any longer.
mShutdownSelfRef = nullptr;
GetManager()->RunWhenGPUProcessRecreated(
NS_NewRunnableFunction("VideoDecoderChild::ActorDestroy", [=]() {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(ref->mRemoteProcessCrashTime);
if (ref->mInitialized) {
mDecodedData = MediaDataDecoder::DecodedData();
mDecodePromise.RejectIfExists(error, __func__);
mDrainPromise.RejectIfExists(error, __func__);
mFlushPromise.RejectIfExists(error, __func__);
mShutdownPromise.ResolveIfExists(true, __func__);
// Make sure the next request will be rejected accordingly if ever
// called.
mNeedNewDecoder = true;
} else {
ref->mInitPromise.RejectIfExists(error, __func__);
}
}));
} else {
MediaResult error(NS_ERROR_DOM_MEDIA_DECODE_ERR);
mDecodePromise.RejectIfExists(error, __func__);
mDrainPromise.RejectIfExists(error, __func__);
mFlushPromise.RejectIfExists(error, __func__);
mShutdownPromise.ResolveIfExists(true, __func__);
RefPtr<RemoteDecoderChild> kungFuDeathGrip = mShutdownSelfRef.forget();
Unused << kungFuDeathGrip;
}
}
mCanSend = false;
RecordShutdownTelemetry(aWhy == AbnormalShutdown);
}
void RemoteDecoderChild::DestroyIPDL() {
@ -105,12 +139,20 @@ void RemoteDecoderChild::IPDLActorDestroyed() { mIPDLSelfRef = nullptr; }
RefPtr<MediaDataDecoder::InitPromise> RemoteDecoderChild::Init() {
AssertOnManagerThread();
if (!mIPDLSelfRef || !mCanSend) {
if (!mIPDLSelfRef) {
return MediaDataDecoder::InitPromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
}
SendInit();
if (mCanSend) {
SendInit();
} else if (!mRecreatedOnCrash) {
return MediaDataDecoder::InitPromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
}
// If we failed to send this, then we'll still resolve the Init promise
// as ActorDestroy handles it.
return mInitPromise.Ensure(__func__);
}
@ -119,9 +161,22 @@ RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Decode(
MediaRawData* aSample) {
AssertOnManagerThread();
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mRemoteProcessCrashTime);
return MediaDataDecoder::DecodePromise::CreateAndReject(error, __func__);
}
if (!mCanSend) {
return MediaDataDecoder::DecodePromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
if (mRecreatedOnCrash) {
// We're here if the IPC channel has died but we're still waiting for the
// RunWhenRecreated task to complete. The decode promise will be rejected
// when that task is run.
return mDecodePromise.Ensure(__func__);
} else {
return MediaDataDecoder::DecodePromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
}
}
// TODO: It would be nice to add an allocator method to
@ -148,21 +203,35 @@ RefPtr<MediaDataDecoder::FlushPromise> RemoteDecoderChild::Flush() {
AssertOnManagerThread();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
if (!mCanSend) {
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mRemoteProcessCrashTime);
return MediaDataDecoder::FlushPromise::CreateAndReject(error, __func__);
}
if (mCanSend) {
SendFlush();
} else if (!mRecreatedOnCrash) {
return MediaDataDecoder::FlushPromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
}
SendFlush();
return mFlushPromise.Ensure(__func__);
}
RefPtr<MediaDataDecoder::DecodePromise> RemoteDecoderChild::Drain() {
AssertOnManagerThread();
if (!mCanSend) {
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mRemoteProcessCrashTime);
return MediaDataDecoder::DecodePromise::CreateAndReject(error, __func__);
}
if (mCanSend) {
SendDrain();
} else if (!mRecreatedOnCrash) {
return MediaDataDecoder::DecodePromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
}
SendDrain();
return mDrainPromise.Ensure(__func__);
}
@ -170,6 +239,11 @@ RefPtr<ShutdownPromise> RemoteDecoderChild::Shutdown() {
AssertOnManagerThread();
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mInitialized = false;
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mRemoteProcessCrashTime);
return ShutdownPromise::CreateAndResolve(true, __func__);
}
if (!mCanSend) {
return ShutdownPromise::CreateAndResolve(true, __func__);
}

Просмотреть файл

@ -19,7 +19,7 @@ class RemoteDecoderChild : public PRemoteDecoderChild,
friend class PRemoteDecoderChild;
public:
explicit RemoteDecoderChild();
explicit RemoteDecoderChild(bool aRecreatedOnCrash = false);
// PRemoteDecoderChild
virtual IPCResult RecvOutput(const DecodedOutputIPDL& aDecodedData) = 0;
@ -28,6 +28,8 @@ class RemoteDecoderChild : public PRemoteDecoderChild,
IPCResult RecvError(const nsresult& aError);
IPCResult RecvInitComplete(const TrackInfo::TrackType& trackType,
const nsCString& aDecoderDescription,
const bool& aHardware,
const nsCString& aHardwareReason,
const ConversionRequired& aConversion);
IPCResult RecvInitFailed(const nsresult& aReason);
IPCResult RecvFlushComplete();
@ -57,6 +59,8 @@ class RemoteDecoderChild : public PRemoteDecoderChild,
virtual ~RemoteDecoderChild();
void AssertOnManagerThread() const;
virtual void RecordShutdownTelemetry(bool aForAbnormalShutdown) {}
RefPtr<RemoteDecoderChild> mIPDLSelfRef;
bool mCanSend = false;
MediaDataDecoder::DecodedData mDecodedData;
@ -70,10 +74,16 @@ class RemoteDecoderChild : public PRemoteDecoderChild,
MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushPromise;
MozPromiseHolder<ShutdownPromise> mShutdownPromise;
TimeStamp mRemoteProcessCrashTime;
nsCString mHardwareAcceleratedReason;
nsCString mDescription;
bool mInitialized = false;
bool mIsHardwareAccelerated = false;
// Set to true if the actor got destroyed and we haven't yet notified the
// caller.
bool mNeedNewDecoder = false;
const bool mRecreatedOnCrash;
MediaDataDecoder::ConversionRequired mConversion =
MediaDataDecoder::ConversionRequired::kNeedNone;
// Keep this instance alive during SendShutdown RecvShutdownComplete

Просмотреть файл

@ -8,15 +8,29 @@
#include "base/task.h"
#include "RemoteDecoderChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/layers/ISurfaceAllocator.h"
namespace mozilla {
using namespace layers;
using namespace gfx;
// Only modified on the main-thread
StaticRefPtr<nsIThread> sRemoteDecoderManagerChildThread;
StaticRefPtr<AbstractThread> sRemoteDecoderManagerChildAbstractThread;
// Only accessed from sRemoteDecoderManagerChildThread
static StaticRefPtr<RemoteDecoderManagerChild> sRemoteDecoderManagerChild;
static StaticRefPtr<RemoteDecoderManagerChild>
sRemoteDecoderManagerChildForRDDProcess;
static StaticRefPtr<RemoteDecoderManagerChild>
sRemoteDecoderManagerChildForGPUProcess;
static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks;
/* static */
void RemoteDecoderManagerChild::InitializeThread() {
@ -30,15 +44,27 @@ void RemoteDecoderManagerChild::InitializeThread() {
sRemoteDecoderManagerChildAbstractThread =
AbstractThread::CreateXPCOMThreadWrapper(childThread, false);
sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>();
}
}
/* static */
void RemoteDecoderManagerChild::InitForContent(
void RemoteDecoderManagerChild::InitForRDDProcess(
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager) {
InitializeThread();
sRemoteDecoderManagerChildThread->Dispatch(
NewRunnableFunction("InitForContentRunnable", &Open,
NewRunnableFunction("InitForContentRunnable", &OpenForRDDProcess,
std::move(aVideoManager)),
NS_DISPATCH_NORMAL);
}
/* static */
void RemoteDecoderManagerChild::InitForGPUProcess(
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager) {
InitializeThread();
sRemoteDecoderManagerChildThread->Dispatch(
NewRunnableFunction("InitForContentRunnable", &OpenForGPUProcess,
std::move(aVideoManager)),
NS_DISPATCH_NORMAL);
}
@ -49,26 +75,54 @@ void RemoteDecoderManagerChild::Shutdown() {
if (sRemoteDecoderManagerChildThread) {
sRemoteDecoderManagerChildThread->Dispatch(
NS_NewRunnableFunction("dom::RemoteDecoderManagerChild::Shutdown",
[]() {
if (sRemoteDecoderManagerChild &&
sRemoteDecoderManagerChild->CanSend()) {
sRemoteDecoderManagerChild->Close();
sRemoteDecoderManagerChild = nullptr;
}
}),
NS_NewRunnableFunction(
"dom::RemoteDecoderManagerChild::Shutdown",
[]() {
if (sRemoteDecoderManagerChildForRDDProcess &&
sRemoteDecoderManagerChildForRDDProcess->CanSend()) {
sRemoteDecoderManagerChildForRDDProcess->Close();
sRemoteDecoderManagerChildForRDDProcess = nullptr;
}
if (sRemoteDecoderManagerChildForGPUProcess &&
sRemoteDecoderManagerChildForGPUProcess->CanSend()) {
sRemoteDecoderManagerChildForGPUProcess->Close();
sRemoteDecoderManagerChildForGPUProcess = nullptr;
}
}),
NS_DISPATCH_NORMAL);
sRemoteDecoderManagerChildAbstractThread = nullptr;
sRemoteDecoderManagerChildThread->Shutdown();
sRemoteDecoderManagerChildThread = nullptr;
sRecreateTasks = nullptr;
}
}
void RemoteDecoderManagerChild::RunWhenGPUProcessRecreated(
already_AddRefed<Runnable> aTask) {
MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
// If we've already been recreated, then run the task immediately.
if (GetGPUProcessSingleton() && GetGPUProcessSingleton() != this &&
GetGPUProcessSingleton()->CanSend()) {
RefPtr<Runnable> task = aTask;
task->Run();
} else {
sRecreateTasks->AppendElement(aTask);
}
}
/* static */
RemoteDecoderManagerChild* RemoteDecoderManagerChild::GetSingleton() {
RemoteDecoderManagerChild* RemoteDecoderManagerChild::GetRDDProcessSingleton() {
MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
return sRemoteDecoderManagerChild;
return sRemoteDecoderManagerChildForRDDProcess;
}
/* static */
RemoteDecoderManagerChild* RemoteDecoderManagerChild::GetGPUProcessSingleton() {
MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
return sRemoteDecoderManagerChildForGPUProcess;
}
/* static */
@ -83,7 +137,9 @@ AbstractThread* RemoteDecoderManagerChild::GetManagerAbstractThread() {
PRemoteDecoderChild* RemoteDecoderManagerChild::AllocPRemoteDecoderChild(
const RemoteDecoderInfoIPDL& /* not used */,
const CreateDecoderParams::OptionSet& /* not used */, bool* /* not used */,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* /* not used */, nsCString* /* not used */,
nsCString* /* not used */) {
// RemoteDecoderModule is responsible for creating RemoteDecoderChild
// classes.
@ -100,7 +156,7 @@ bool RemoteDecoderManagerChild::DeallocPRemoteDecoderChild(
return true;
}
void RemoteDecoderManagerChild::Open(
void RemoteDecoderManagerChild::OpenForRDDProcess(
Endpoint<PRemoteDecoderManagerChild>&& aEndpoint) {
MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
// Only create RemoteDecoderManagerChild, bind new endpoint and init
@ -109,19 +165,38 @@ void RemoteDecoderManagerChild::Open(
// or
// 2) if ActorDestroy was called (mCanSend is false) meaning the other
// end of the ipc channel was torn down
if (sRemoteDecoderManagerChild && sRemoteDecoderManagerChild->mCanSend) {
if (sRemoteDecoderManagerChildForRDDProcess &&
sRemoteDecoderManagerChildForRDDProcess->mCanSend) {
return;
}
sRemoteDecoderManagerChild = nullptr;
sRemoteDecoderManagerChildForRDDProcess = nullptr;
if (aEndpoint.IsValid()) {
RefPtr<RemoteDecoderManagerChild> manager = new RemoteDecoderManagerChild();
if (aEndpoint.Bind(manager)) {
sRemoteDecoderManagerChild = manager;
sRemoteDecoderManagerChildForRDDProcess = manager;
manager->InitIPDL();
}
}
}
void RemoteDecoderManagerChild::OpenForGPUProcess(
Endpoint<PRemoteDecoderManagerChild>&& aEndpoint) {
// Make sure we always dispatch everything in sRecreateTasks, even if we
// fail since this is as close to being recreated as we will ever be.
sRemoteDecoderManagerChildForGPUProcess = nullptr;
if (aEndpoint.IsValid()) {
RefPtr<RemoteDecoderManagerChild> manager = new RemoteDecoderManagerChild();
if (aEndpoint.Bind(manager)) {
sRemoteDecoderManagerChildForGPUProcess = manager;
manager->InitIPDL();
}
}
for (Runnable* task : *sRecreateTasks) {
task->Run();
}
sRecreateTasks->Clear();
}
void RemoteDecoderManagerChild::InitIPDL() {
mCanSend = true;
mIPDLSelfRef = this;
@ -138,4 +213,99 @@ bool RemoteDecoderManagerChild::CanSend() {
return mCanSend;
}
bool RemoteDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) {
if (NS_GetCurrentThread() != sRemoteDecoderManagerChildThread) {
RefPtr<RemoteDecoderManagerChild> self = this;
mozilla::ipc::Shmem shmem = aShmem;
sRemoteDecoderManagerChildThread->Dispatch(
NS_NewRunnableFunction("RemoteDecoderManagerChild::DeallocShmem",
[self, shmem]() {
if (self->CanSend()) {
mozilla::ipc::Shmem shmemCopy = shmem;
self->DeallocShmem(shmemCopy);
}
}),
NS_DISPATCH_NORMAL);
return true;
}
return PRemoteDecoderManagerChild::DeallocShmem(aShmem);
}
struct SurfaceDescriptorUserData {
SurfaceDescriptorUserData(RemoteDecoderManagerChild* aAllocator,
SurfaceDescriptor& aSD)
: mAllocator(aAllocator), mSD(aSD) {}
~SurfaceDescriptorUserData() { DestroySurfaceDescriptor(mAllocator, &mSD); }
RefPtr<RemoteDecoderManagerChild> mAllocator;
SurfaceDescriptor mSD;
};
void DeleteSurfaceDescriptorUserData(void* aClosure) {
SurfaceDescriptorUserData* sd =
reinterpret_cast<SurfaceDescriptorUserData*>(aClosure);
delete sd;
}
already_AddRefed<SourceSurface> RemoteDecoderManagerChild::Readback(
const SurfaceDescriptorGPUVideo& aSD) {
// We can't use NS_DISPATCH_SYNC here since that can spin the event
// loop while it waits. This function can be called from JS and we
// don't want that to happen.
SynchronousTask task("Readback sync");
RefPtr<RemoteDecoderManagerChild> ref = this;
SurfaceDescriptor sd;
if (NS_FAILED(sRemoteDecoderManagerChildThread->Dispatch(
NS_NewRunnableFunction("RemoteDecoderManagerChild::Readback",
[&]() {
AutoCompleteTask complete(&task);
if (ref->CanSend()) {
ref->SendReadback(aSD, &sd);
}
}),
NS_DISPATCH_NORMAL))) {
return nullptr;
}
task.Wait();
if (!IsSurfaceDescriptorValid(sd)) {
return nullptr;
}
RefPtr<DataSourceSurface> source = GetSurfaceForDescriptor(sd);
if (!source) {
DestroySurfaceDescriptor(this, &sd);
NS_WARNING("Failed to map SurfaceDescriptor in Readback");
return nullptr;
}
static UserDataKey sSurfaceDescriptor;
source->AddUserData(&sSurfaceDescriptor,
new SurfaceDescriptorUserData(this, sd),
DeleteSurfaceDescriptorUserData);
return source.forget();
}
void RemoteDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD) {
RefPtr<RemoteDecoderManagerChild> ref = this;
SurfaceDescriptorGPUVideo sd = std::move(aSD);
sRemoteDecoderManagerChildThread->Dispatch(
NS_NewRunnableFunction(
"RemoteDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo",
[ref, sd]() {
if (ref->CanSend()) {
ref->SendDeallocateSurfaceDescriptorGPUVideo(sd);
}
}),
NS_DISPATCH_NORMAL);
}
void RemoteDecoderManagerChild::HandleFatalError(const char* aMsg) const {
dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
}
} // namespace mozilla

Просмотреть файл

@ -9,24 +9,57 @@
namespace mozilla {
class RemoteDecoderManagerChild final : public PRemoteDecoderManagerChild {
class RemoteDecoderManagerChild final : public PRemoteDecoderManagerChild,
public mozilla::ipc::IShmemAllocator {
friend class PRemoteDecoderManagerChild;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderManagerChild)
// Can only be called from the manager thread
static RemoteDecoderManagerChild* GetSingleton();
static RemoteDecoderManagerChild* GetRDDProcessSingleton();
static RemoteDecoderManagerChild* GetGPUProcessSingleton();
// Can be called from any thread.
static nsIThread* GetManagerThread();
static AbstractThread* GetManagerAbstractThread();
// Can be called from any thread, dispatches the request to the IPDL thread
// internally and will be ignored if the IPDL actor has been destroyed.
already_AddRefed<gfx::SourceSurface> Readback(
const SurfaceDescriptorGPUVideo& aSD);
void DeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD);
bool AllocShmem(size_t aSize,
mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
mozilla::ipc::Shmem* aShmem) override {
return PRemoteDecoderManagerChild::AllocShmem(aSize, aShmType, aShmem);
}
bool AllocUnsafeShmem(size_t aSize,
mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
mozilla::ipc::Shmem* aShmem) override {
return PRemoteDecoderManagerChild::AllocUnsafeShmem(aSize, aShmType,
aShmem);
}
// Can be called from any thread, dispatches the request to the IPDL thread
// internally and will be ignored if the IPDL actor has been destroyed.
bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
// Main thread only
static void InitForContent(
static void InitForRDDProcess(
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager);
static void InitForGPUProcess(
Endpoint<PRemoteDecoderManagerChild>&& aVideoManager);
static void Shutdown();
// Run aTask (on the manager thread) when we next attempt to create a new
// manager (even if creation fails). Intended to be called from ActorDestroy
// when we get notified that the old manager is being destroyed. Can only be
// called from the manager thread.
void RunWhenGPUProcessRecreated(already_AddRefed<Runnable> aTask);
bool CanSend();
protected:
@ -35,9 +68,13 @@ class RemoteDecoderManagerChild final : public PRemoteDecoderManagerChild {
void ActorDestroy(ActorDestroyReason aWhy) override;
void ActorDealloc() override;
void HandleFatalError(const char* aMsg) const override;
PRemoteDecoderChild* AllocPRemoteDecoderChild(
const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
const CreateDecoderParams::OptionSet& aOptions, bool* aSuccess,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* aBlacklistedD3D11Driver, nsCString* aBlacklistedD3D9Driver,
nsCString* aErrorDescription);
bool DeallocPRemoteDecoderChild(PRemoteDecoderChild* actor);
@ -48,7 +85,10 @@ class RemoteDecoderManagerChild final : public PRemoteDecoderManagerChild {
RemoteDecoderManagerChild() = default;
~RemoteDecoderManagerChild() = default;
static void Open(Endpoint<PRemoteDecoderManagerChild>&& aEndpoint);
static void OpenForRDDProcess(
Endpoint<PRemoteDecoderManagerChild>&& aEndpoint);
static void OpenForGPUProcess(
Endpoint<PRemoteDecoderManagerChild>&& aEndpoint);
RefPtr<RemoteDecoderManagerChild> mIPDLSelfRef;

Просмотреть файл

@ -13,11 +13,35 @@
#include "RemoteVideoDecoder.h"
#include "VideoUtils.h" // for MediaThreadType
#include "ImageContainer.h"
#include "mozilla/layers/VideoBridgeChild.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/SyncRunnable.h"
namespace mozilla {
#ifdef XP_WIN
extern const nsCString GetFoundD3D11BlacklistedDLL();
extern const nsCString GetFoundD3D9BlacklistedDLL();
#endif // XP_WIN
using namespace ipc;
using namespace layers;
using namespace gfx;
StaticRefPtr<nsIThread> sRemoteDecoderManagerParentThread;
StaticRefPtr<TaskQueue> sRemoteDecoderManagerTaskQueue;
SurfaceDescriptorGPUVideo RemoteDecoderManagerParent::StoreImage(
Image* aImage, TextureClient* aTexture) {
SurfaceDescriptorGPUVideo ret;
aTexture->GPUVideoDesc(&ret);
mImageMap[ret.handle()] = aImage;
mTextureMap[ret.handle()] = aTexture;
return ret;
}
class RemoteDecoderManagerThreadHolder {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteDecoderManagerThreadHolder)
@ -88,6 +112,12 @@ bool RemoteDecoderManagerParent::StartupThreads() {
}),
NS_DISPATCH_NORMAL);
#endif
if (XRE_IsGPUProcess()) {
sRemoteDecoderManagerParentThread->Dispatch(
NS_NewRunnableFunction("RemoteDecoderManagerParent::StartupThreads",
[]() { layers::VideoBridgeChild::Startup(); }),
NS_DISPATCH_NORMAL);
}
sRemoteDecoderManagerTaskQueue = new TaskQueue(
managerThread.forget(),
@ -107,13 +137,23 @@ void RemoteDecoderManagerParent::ShutdownThreads() {
}
}
void RemoteDecoderManagerParent::ShutdownVideoBridge() {
if (sRemoteDecoderManagerParentThread) {
RefPtr<Runnable> task = NS_NewRunnableFunction(
"RemoteDecoderManagerParent::ShutdownVideoBridge",
[]() { VideoBridgeChild::Shutdown(); });
SyncRunnable::DispatchToThread(sRemoteDecoderManagerParentThread, task);
}
}
bool RemoteDecoderManagerParent::OnManagerThread() {
return NS_GetCurrentThread() == sRemoteDecoderManagerParentThread;
}
bool RemoteDecoderManagerParent::CreateForContent(
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD);
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_RDD ||
XRE_GetProcessType() == GeckoProcessType_GPU);
MOZ_ASSERT(NS_IsMainThread());
if (!StartupThreads()) {
@ -149,19 +189,26 @@ void RemoteDecoderManagerParent::ActorDestroy(
PRemoteDecoderParent* RemoteDecoderManagerParent::AllocPRemoteDecoderParent(
const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
const CreateDecoderParams::OptionSet& aOptions, bool* aSuccess,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* aBlacklistedD3D11Driver, nsCString* aBlacklistedD3D9Driver,
nsCString* aErrorDescription) {
RefPtr<TaskQueue> decodeTaskQueue =
new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
"RemoteVideoDecoderParent::mDecodeTaskQueue");
#ifdef XP_WIN
*aBlacklistedD3D11Driver = GetFoundD3D11BlacklistedDLL();
*aBlacklistedD3D9Driver = GetFoundD3D9BlacklistedDLL();
#endif // XP_WIN
if (aRemoteDecoderInfo.type() ==
RemoteDecoderInfoIPDL::TVideoDecoderInfoIPDL) {
const VideoDecoderInfoIPDL& decoderInfo =
aRemoteDecoderInfo.get_VideoDecoderInfoIPDL();
return new RemoteVideoDecoderParent(
this, decoderInfo.videoInfo(), decoderInfo.framerate(), aOptions,
sRemoteDecoderManagerTaskQueue, decodeTaskQueue, aSuccess,
aIdentifier, sRemoteDecoderManagerTaskQueue, decodeTaskQueue, aSuccess,
aErrorDescription);
} else if (aRemoteDecoderInfo.type() == RemoteDecoderInfoIPDL::TAudioInfo) {
return new RemoteAudioDecoderParent(
@ -192,4 +239,54 @@ void RemoteDecoderManagerParent::Open(
void RemoteDecoderManagerParent::ActorDealloc() { Release(); }
mozilla::ipc::IPCResult RemoteDecoderManagerParent::RecvReadback(
const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) {
RefPtr<Image> image = mImageMap[aSD.handle()];
if (!image) {
*aResult = null_t();
return IPC_OK();
}
RefPtr<SourceSurface> source = image->GetAsSourceSurface();
if (!source) {
*aResult = null_t();
return IPC_OK();
}
SurfaceFormat format = source->GetFormat();
IntSize size = source->GetSize();
size_t length = ImageDataSerializer::ComputeRGBBufferSize(size, format);
Shmem buffer;
if (!length ||
!AllocShmem(length, Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
*aResult = null_t();
return IPC_OK();
}
RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
gfx::BackendType::CAIRO, buffer.get<uint8_t>(), size,
ImageDataSerializer::ComputeRGBStride(format, size.width), format);
if (!dt) {
DeallocShmem(buffer);
*aResult = null_t();
return IPC_OK();
}
dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
dt->Flush();
*aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format, true),
MemoryOrShmem(std::move(buffer)));
return IPC_OK();
}
mozilla::ipc::IPCResult
RemoteDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD) {
mImageMap.erase(aSD.handle());
mTextureMap.erase(aSD.handle());
return IPC_OK();
}
} // namespace mozilla

Просмотреть файл

@ -20,18 +20,31 @@ class RemoteDecoderManagerParent final : public PRemoteDecoderManagerParent {
static bool CreateForContent(
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
// Can be called from any thread
SurfaceDescriptorGPUVideo StoreImage(layers::Image* aImage,
layers::TextureClient* aTexture);
static bool StartupThreads();
static void ShutdownThreads();
static void ShutdownVideoBridge();
bool OnManagerThread();
protected:
PRemoteDecoderParent* AllocPRemoteDecoderParent(
const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
const CreateDecoderParams::OptionSet& aOptions, bool* aSuccess,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* aBlacklistedD3D11Driver, nsCString* aBlacklistedD3D9Driver,
nsCString* aErrorDescription);
bool DeallocPRemoteDecoderParent(PRemoteDecoderParent* actor);
mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD,
SurfaceDescriptor* aResult);
mozilla::ipc::IPCResult RecvDeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD);
void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
void ActorDealloc() override;
@ -43,6 +56,9 @@ class RemoteDecoderManagerParent final : public PRemoteDecoderManagerParent {
void Open(Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
std::map<uint64_t, RefPtr<layers::Image>> mImageMap;
std::map<uint64_t, RefPtr<layers::TextureClient>> mTextureMap;
RefPtr<RemoteDecoderManagerThreadHolder> mThreadHolder;
};

Просмотреть файл

@ -71,8 +71,9 @@ void RemoteDecoderModule::LaunchRDDProcessIfNeeded() {
if (mManagerThread) {
RefPtr<Runnable> task = NS_NewRunnableFunction(
"RemoteDecoderModule::LaunchRDDProcessIfNeeded-CheckSend", [&]() {
if (RemoteDecoderManagerChild::GetSingleton()) {
needsLaunch = !RemoteDecoderManagerChild::GetSingleton()->CanSend();
if (RemoteDecoderManagerChild::GetRDDProcessSingleton()) {
needsLaunch =
!RemoteDecoderManagerChild::GetRDDProcessSingleton()->CanSend();
}
});
SyncRunnable::DispatchToThread(mManagerThread, task);

Просмотреть файл

@ -47,9 +47,12 @@ mozilla::ipc::IPCResult RemoteDecoderParent::RecvInit() {
MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
aTrack == TrackInfo::kVideoTrack);
if (self->mDecoder) {
Unused << self->SendInitComplete(aTrack,
self->mDecoder->GetDescriptionName(),
self->mDecoder->NeedsConversion());
nsCString hardwareReason;
bool hardwareAccelerated =
self->mDecoder->IsHardwareAccelerated(hardwareReason);
Unused << self->SendInitComplete(
aTrack, self->mDecoder->GetDescriptionName(), hardwareAccelerated,
hardwareReason, self->mDecoder->NeedsConversion());
}
},
[self](MediaResult aReason) {

Просмотреть файл

@ -11,15 +11,44 @@
# include "AOMDecoder.h"
# include "DAV1DDecoder.h"
#endif
#ifdef XP_WIN
# include "WMFDecoderModule.h"
#endif
#include "ImageContainer.h" // for PlanarYCbCrData and BufferRecycleBin
#include "mozilla/layers/VideoBridgeChild.h"
#include "mozilla/layers/ImageClient.h"
#include "PDMFactory.h"
#include "RemoteDecoderManagerChild.h"
#include "RemoteDecoderManagerParent.h"
#include "GPUVideoImage.h"
#include "MediaInfo.h"
#include "mozilla/Telemetry.h"
#include "mozilla/layers/TextureClient.h"
namespace mozilla {
using namespace layers; // for PlanarYCbCrData and BufferRecycleBin
using namespace ipc;
using namespace gfx;
RemoteVideoDecoderChild::RemoteVideoDecoderChild()
: RemoteDecoderChild(), mBufferRecycleBin(new BufferRecycleBin) {}
class KnowsCompositorVideo : public layers::KnowsCompositor {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorVideo, override)
layers::TextureForwarder* GetTextureForwarder() override {
return VideoBridgeChild::GetSingleton();
}
layers::LayersIPCActor* GetLayersIPCActor() override {
return VideoBridgeChild::GetSingleton();
}
private:
virtual ~KnowsCompositorVideo() = default;
};
RemoteVideoDecoderChild::RemoteVideoDecoderChild(bool aRecreatedOnCrash)
: RemoteDecoderChild(aRecreatedOnCrash),
mBufferRecycleBin(new BufferRecycleBin) {}
RefPtr<mozilla::layers::Image> RemoteVideoDecoderChild::DeserializeImage(
const SurfaceDescriptorBuffer& aSdBuffer, const IntSize& aPicSize) {
@ -92,7 +121,8 @@ mozilla::ipc::IPCResult RemoteVideoDecoderChild::RecvOutput(
MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TRemoteVideoDataIPDL);
const RemoteVideoDataIPDL& aData = aDecodedData.get_RemoteVideoDataIPDL();
RefPtr<Image> image = DeserializeImage(aData.sdBuffer(), aData.frameSize());
RefPtr<Image> image = DeserializeImage(
aData.sd().get_SurfaceDescriptorBuffer(), aData.frameSize());
RefPtr<VideoData> video = VideoData::CreateFromImage(
aData.display(), aData.base().offset(), aData.base().time(),
@ -107,7 +137,7 @@ MediaResult RemoteVideoDecoderChild::InitIPDL(
const VideoInfo& aVideoInfo, float aFramerate,
const CreateDecoderParams::OptionSet& aOptions) {
RefPtr<RemoteDecoderManagerChild> manager =
RemoteDecoderManagerChild::GetSingleton();
RemoteDecoderManagerChild::GetRDDProcessSingleton();
// The manager isn't available because RemoteDecoderManagerChild has been
// initialized with null end points and we don't want to decode video on RDD
@ -125,9 +155,101 @@ MediaResult RemoteVideoDecoderChild::InitIPDL(
mIPDLSelfRef = this;
bool success = false;
nsCString errorDescription;
nsCString blacklistedD3D11Driver;
nsCString blacklistedD3D9Driver;
VideoDecoderInfoIPDL decoderInfo(aVideoInfo, aFramerate);
if (manager->SendPRemoteDecoderConstructor(this, decoderInfo, aOptions,
&success, &errorDescription)) {
TextureFactoryIdentifier defaultIdent;
if (manager->SendPRemoteDecoderConstructor(
this, decoderInfo, aOptions, defaultIdent, &success,
&blacklistedD3D11Driver, &blacklistedD3D9Driver, &errorDescription)) {
mCanSend = true;
}
return success ? MediaResult(NS_OK)
: MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription);
}
#ifdef XP_WIN
static void ReportUnblacklistingTelemetry(
bool isGPUProcessCrashed, const nsCString& aD3D11BlacklistedDriver,
const nsCString& aD3D9BlacklistedDriver) {
const nsCString& blacklistedDLL = !aD3D11BlacklistedDriver.IsEmpty()
? aD3D11BlacklistedDriver
: aD3D9BlacklistedDriver;
if (!blacklistedDLL.IsEmpty()) {
Telemetry::Accumulate(
Telemetry::VIDEO_UNBLACKINGLISTING_DXVA_DRIVER_RUNTIME_STATUS,
blacklistedDLL, isGPUProcessCrashed ? 1 : 0);
}
}
#endif // XP_WIN
GpuRemoteVideoDecoderChild::GpuRemoteVideoDecoderChild()
: RemoteVideoDecoderChild(true) {}
mozilla::ipc::IPCResult GpuRemoteVideoDecoderChild::RecvOutput(
const DecodedOutputIPDL& aDecodedData) {
AssertOnManagerThread();
MOZ_ASSERT(aDecodedData.type() == DecodedOutputIPDL::TRemoteVideoDataIPDL);
const RemoteVideoDataIPDL& aData = aDecodedData.get_RemoteVideoDataIPDL();
// The Image here creates a TextureData object that takes ownership
// of the SurfaceDescriptor, and is responsible for making sure that
// it gets deallocated.
RefPtr<Image> image =
new GPUVideoImage(GetManager(), aData.sd(), aData.frameSize());
RefPtr<VideoData> video = VideoData::CreateFromImage(
aData.display(), aData.base().offset(), aData.base().time(),
aData.base().duration(), image, aData.base().keyframe(),
aData.base().timecode());
mDecodedData.AppendElement(std::move(video));
return IPC_OK();
}
void GpuRemoteVideoDecoderChild::RecordShutdownTelemetry(
bool aAbnormalShutdown) {
#ifdef XP_WIN
ReportUnblacklistingTelemetry(aAbnormalShutdown, mBlacklistedD3D11Driver,
mBlacklistedD3D9Driver);
#endif // XP_WIN
}
MediaResult GpuRemoteVideoDecoderChild::InitIPDL(
const VideoInfo& aVideoInfo, float aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier) {
RefPtr<RemoteDecoderManagerChild> manager =
RemoteDecoderManagerChild::GetGPUProcessSingleton();
// The manager isn't available because RemoteDecoderManagerChild has been
// initialized with null end points and we don't want to decode video on GPU
// process anymore. Return false here so that we can fallback to other PDMs.
if (!manager) {
return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
RESULT_DETAIL("RemoteDecoderManager is not available."));
}
// The manager doesn't support sending messages because we've just crashed
// and are working on reinitialization. Don't initialize mIPDLSelfRef and
// leave us in an error state. We'll then immediately reject the promise when
// Init() is called and the caller can try again. Hopefully by then the new
// manager is ready, or we've notified the caller of it being no longer
// available. If not, then the cycle repeats until we're ready.
if (!manager->CanSend()) {
return NS_OK;
}
mIPDLSelfRef = this;
bool success = false;
nsCString errorDescription;
VideoDecoderInfoIPDL decoderInfo(aVideoInfo, aFramerate);
if (manager->SendPRemoteDecoderConstructor(
this, decoderInfo, aOptions, aIdentifier, &success,
&mBlacklistedD3D11Driver, &mBlacklistedD3D9Driver,
&errorDescription)) {
mCanSend = true;
}
@ -138,18 +260,42 @@ MediaResult RemoteVideoDecoderChild::InitIPDL(
RemoteVideoDecoderParent::RemoteVideoDecoderParent(
RemoteDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier,
TaskQueue* aManagerTaskQueue, TaskQueue* aDecodeTaskQueue, bool* aSuccess,
nsCString* aErrorDescription)
: RemoteDecoderParent(aParent, aManagerTaskQueue, aDecodeTaskQueue),
mVideoInfo(aVideoInfo) {
if (XRE_IsGPUProcess()) {
mKnowsCompositor = new KnowsCompositorVideo();
mKnowsCompositor->IdentifyTextureHost(aIdentifier);
}
CreateDecoderParams params(mVideoInfo);
params.mTaskQueue = mDecodeTaskQueue;
params.mKnowsCompositor = mKnowsCompositor;
params.mImageContainer = new layers::ImageContainer();
params.mRate = CreateDecoderParams::VideoFrameRate(aFramerate);
params.mOptions = aOptions;
MediaResult error(NS_OK);
params.mError = &error;
if (XRE_IsGPUProcess()) {
#ifdef XP_WIN
// Ensure everything is properly initialized on the right thread.
PDMFactory::EnsureInit();
// TODO: Ideally we wouldn't hardcode the WMF PDM, and we'd use the normal
// PDM factory logic for picking a decoder.
RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
pdm->Startup();
mDecoder = pdm->CreateVideoDecoder(params);
#else
MOZ_ASSERT(false,
"Can't use RemoteVideoDecoder in the GPU process on non-Windows "
"platforms yet");
#endif
}
#ifdef MOZ_AV1
if (AOMDecoder::IsAV1(params.mConfig.mMimeType)) {
if (StaticPrefs::MediaAv1UseDav1d()) {
@ -172,6 +318,11 @@ MediaResult RemoteVideoDecoderParent::ProcessDecodedData(
const MediaDataDecoder::DecodedData& aData) {
MOZ_ASSERT(OnManagerThread());
// If the video decoder bridge has shut down, stop.
if (mKnowsCompositor && !mKnowsCompositor->GetTextureForwarder()) {
return NS_OK;
}
for (const auto& data : aData) {
MOZ_ASSERT(data->mType == MediaData::Type::VIDEO_DATA,
"Can only decode videos using RemoteDecoderParent!");
@ -181,30 +332,55 @@ MediaResult RemoteVideoDecoderParent::ProcessDecodedData(
"Decoded video must output a layer::Image to "
"be used with RemoteDecoderParent");
PlanarYCbCrImage* image =
static_cast<PlanarYCbCrImage*>(video->mImage.get());
SurfaceDescriptor sd;
IntSize size;
SurfaceDescriptorBuffer sdBuffer;
Shmem buffer;
if (!AllocShmem(image->GetDataSize(), Shmem::SharedMemory::TYPE_BASIC,
&buffer)) {
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
"AllocShmem failed in "
"RemoteVideoDecoderParent::ProcessDecodedData");
}
if (image->GetDataSize() > buffer.Size<uint8_t>()) {
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
"AllocShmem returned less than requested in "
"RemoteVideoDecoderParent::ProcessDecodedData");
}
if (mKnowsCompositor) {
RefPtr<TextureClient> texture =
video->mImage->GetTextureClient(mKnowsCompositor);
sdBuffer.data() = std::move(buffer);
image->BuildSurfaceDescriptorBuffer(sdBuffer);
if (!texture) {
texture = ImageClient::CreateTextureClientForImage(video->mImage,
mKnowsCompositor);
}
if (texture && !texture->IsAddedToCompositableClient()) {
texture->InitIPDLActor(mKnowsCompositor);
texture->SetAddedToCompositableClient();
}
if (texture) {
sd = mParent->StoreImage(video->mImage, texture);
size = texture->GetSize();
}
} else {
PlanarYCbCrImage* image =
static_cast<PlanarYCbCrImage*>(video->mImage.get());
SurfaceDescriptorBuffer sdBuffer;
Shmem buffer;
if (!AllocShmem(image->GetDataSize(), Shmem::SharedMemory::TYPE_BASIC,
&buffer)) {
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
"AllocShmem failed in "
"RemoteVideoDecoderParent::ProcessDecodedData");
}
if (image->GetDataSize() > buffer.Size<uint8_t>()) {
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
"AllocShmem returned less than requested in "
"RemoteVideoDecoderParent::ProcessDecodedData");
}
sdBuffer.data() = std::move(buffer);
image->BuildSurfaceDescriptorBuffer(sdBuffer);
sd = sdBuffer;
size = image->GetSize();
}
RemoteVideoDataIPDL output(
MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
data->mDuration, data->mKeyframe),
video->mDisplay, image->GetSize(), sdBuffer, video->mFrameID);
video->mDisplay, size, sd, video->mFrameID);
Unused << SendOutput(output);
}

Просмотреть файл

@ -16,11 +16,12 @@ class BufferRecycleBin;
namespace mozilla {
class KnowsCompositorVideo;
using mozilla::ipc::IPCResult;
class RemoteVideoDecoderChild final : public RemoteDecoderChild {
class RemoteVideoDecoderChild : public RemoteDecoderChild {
public:
explicit RemoteVideoDecoderChild();
explicit RemoteVideoDecoderChild(bool aRecreatedOnCrash = false);
MOZ_IS_CLASS_INIT
MediaResult InitIPDL(const VideoInfo& aVideoInfo, float aFramerate,
@ -35,11 +36,30 @@ class RemoteVideoDecoderChild final : public RemoteDecoderChild {
RefPtr<mozilla::layers::BufferRecycleBin> mBufferRecycleBin;
};
class GpuRemoteVideoDecoderChild final : public RemoteVideoDecoderChild {
public:
explicit GpuRemoteVideoDecoderChild();
MOZ_IS_CLASS_INIT
MediaResult InitIPDL(const VideoInfo& aVideoInfo, float aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier);
IPCResult RecvOutput(const DecodedOutputIPDL& aDecodedData) override;
void RecordShutdownTelemetry(bool aAbnormalShutdown) override;
private:
nsCString mBlacklistedD3D11Driver;
nsCString mBlacklistedD3D9Driver;
};
class RemoteVideoDecoderParent final : public RemoteDecoderParent {
public:
RemoteVideoDecoderParent(RemoteDecoderManagerParent* aParent,
const VideoInfo& aVideoInfo, float aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier,
TaskQueue* aManagerTaskQueue,
TaskQueue* aDecodeTaskQueue, bool* aSuccess,
nsCString* aErrorDescription);
@ -55,6 +75,8 @@ class RemoteVideoDecoderParent final : public RemoteDecoderParent {
// passed a deserialized VideoInfo from RecvPRemoteDecoderConstructor
// which is destroyed when RecvPRemoteDecoderConstructor returns.
const VideoInfo mVideoInfo;
RefPtr<KnowsCompositorVideo> mKnowsCompositor;
};
} // namespace mozilla

Просмотреть файл

@ -1,350 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VideoDecoderChild.h"
#include "GPUVideoImage.h"
#include "ImageContainer.h"
#include "MediaInfo.h"
#include "VideoDecoderManagerChild.h"
#include "base/thread.h"
#include "mozilla/Telemetry.h"
#include "mozilla/layers/TextureClient.h"
namespace mozilla {
using base::Thread;
using namespace ipc;
using namespace layers;
using namespace gfx;
#ifdef XP_WIN
static void ReportUnblacklistingTelemetry(
bool isGPUProcessCrashed, const nsCString& aD3D11BlacklistedDriver,
const nsCString& aD3D9BlacklistedDriver) {
const nsCString& blacklistedDLL = !aD3D11BlacklistedDriver.IsEmpty()
? aD3D11BlacklistedDriver
: aD3D9BlacklistedDriver;
if (!blacklistedDLL.IsEmpty()) {
Telemetry::Accumulate(
Telemetry::VIDEO_UNBLACKINGLISTING_DXVA_DRIVER_RUNTIME_STATUS,
blacklistedDLL, isGPUProcessCrashed ? 1 : 0);
}
}
#endif // XP_WIN
VideoDecoderChild::VideoDecoderChild()
: mThread(VideoDecoderManagerChild::GetManagerThread()),
mCanSend(false),
mInitialized(false),
mIsHardwareAccelerated(false),
mConversion(MediaDataDecoder::ConversionRequired::kNeedNone),
mNeedNewDecoder(false) {}
VideoDecoderChild::~VideoDecoderChild() {
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvOutput(
const VideoDataIPDL& aData) {
AssertOnManagerThread();
// The Image here creates a TextureData object that takes ownership
// of the SurfaceDescriptor, and is responsible for making sure that
// it gets deallocated.
RefPtr<Image> image =
new GPUVideoImage(GetManager(), aData.sd(), aData.frameSize());
RefPtr<VideoData> video = VideoData::CreateFromImage(
aData.display(), aData.base().offset(), aData.base().time(),
aData.base().duration(), image, aData.base().keyframe(),
aData.base().timecode());
mDecodedData.AppendElement(std::move(video));
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvInputExhausted() {
AssertOnManagerThread();
mDecodePromise.ResolveIfExists(std::move(mDecodedData), __func__);
mDecodedData = MediaDataDecoder::DecodedData();
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvDrainComplete() {
AssertOnManagerThread();
mDrainPromise.ResolveIfExists(std::move(mDecodedData), __func__);
mDecodedData = MediaDataDecoder::DecodedData();
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvError(const nsresult& aError) {
AssertOnManagerThread();
mDecodedData = MediaDataDecoder::DecodedData();
mDecodePromise.RejectIfExists(aError, __func__);
mDrainPromise.RejectIfExists(aError, __func__);
mFlushPromise.RejectIfExists(aError, __func__);
mShutdownPromise.ResolveIfExists(true, __func__);
RefPtr<VideoDecoderChild> kungFuDeathGrip = mShutdownSelfRef.forget();
Unused << kungFuDeathGrip;
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvInitComplete(
const nsCString& aDecoderDescription, const bool& aHardware,
const nsCString& aHardwareReason, const uint32_t& aConversion) {
AssertOnManagerThread();
mInitPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
mInitialized = true;
mDescription = aDecoderDescription;
mIsHardwareAccelerated = aHardware;
mHardwareAcceleratedReason = aHardwareReason;
mConversion = static_cast<MediaDataDecoder::ConversionRequired>(aConversion);
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvInitFailed(
const nsresult& aReason) {
AssertOnManagerThread();
mInitPromise.RejectIfExists(aReason, __func__);
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvFlushComplete() {
AssertOnManagerThread();
mFlushPromise.ResolveIfExists(true, __func__);
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderChild::RecvShutdownComplete() {
AssertOnManagerThread();
MOZ_ASSERT(mShutdownSelfRef);
mShutdownPromise.ResolveIfExists(true, __func__);
RefPtr<VideoDecoderChild> kungFuDeathGrip = mShutdownSelfRef.forget();
Unused << kungFuDeathGrip;
return IPC_OK();
}
void VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
if (aWhy == AbnormalShutdown) {
// GPU process crashed, record the time and send back to MFR for telemetry.
mGPUCrashTime = TimeStamp::Now();
// Defer reporting an error until we've recreated the manager so that
// it'll be safe for MediaFormatReader to recreate decoders
RefPtr<VideoDecoderChild> ref = this;
// Make sure shutdown self reference is null. Since ref is captured by the
// lambda it is not necessary to keep it any longer.
mShutdownSelfRef = nullptr;
GetManager()->RunWhenRecreated(
NS_NewRunnableFunction("VideoDecoderChild::ActorDestroy", [=]() {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(ref->mGPUCrashTime);
if (ref->mInitialized) {
mDecodedData = MediaDataDecoder::DecodedData();
mDecodePromise.RejectIfExists(error, __func__);
mDrainPromise.RejectIfExists(error, __func__);
mFlushPromise.RejectIfExists(error, __func__);
mShutdownPromise.ResolveIfExists(true, __func__);
// Make sure the next request will be rejected accordingly if ever
// called.
mNeedNewDecoder = true;
} else {
ref->mInitPromise.RejectIfExists(error, __func__);
}
}));
}
mCanSend = false;
#ifdef XP_WIN
ReportUnblacklistingTelemetry(aWhy == AbnormalShutdown,
mBlacklistedD3D11Driver,
mBlacklistedD3D9Driver);
#endif // XP_WIN
}
MediaResult VideoDecoderChild::InitIPDL(
const VideoInfo& aVideoInfo, float aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier) {
RefPtr<VideoDecoderManagerChild> manager =
VideoDecoderManagerChild::GetSingleton();
// The manager isn't available because VideoDecoderManagerChild has been
// initialized with null end points and we don't want to decode video on GPU
// process anymore. Return false here so that we can fallback to other PDMs.
if (!manager) {
return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
RESULT_DETAIL("VideoDecoderManager is not available."));
}
// The manager doesn't support sending messages because we've just crashed
// and are working on reinitialization. Don't initialize mIPDLSelfRef and
// leave us in an error state. We'll then immediately reject the promise when
// Init() is called and the caller can try again. Hopefully by then the new
// manager is ready, or we've notified the caller of it being no longer
// available. If not, then the cycle repeats until we're ready.
if (!manager->CanSend()) {
return NS_OK;
}
mIPDLSelfRef = this;
bool success = false;
nsCString errorDescription;
if (manager->SendPVideoDecoderConstructor(
this, aVideoInfo, aFramerate, aOptions, aIdentifier, &success,
&mBlacklistedD3D11Driver, &mBlacklistedD3D9Driver,
&errorDescription)) {
mCanSend = true;
}
return success ? MediaResult(NS_OK)
: MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, errorDescription);
}
void VideoDecoderChild::DestroyIPDL() {
AssertOnManagerThread();
if (mCanSend) {
PVideoDecoderChild::Send__delete__(this);
}
}
void VideoDecoderChild::IPDLActorDestroyed() { mIPDLSelfRef = nullptr; }
// MediaDataDecoder methods
RefPtr<MediaDataDecoder::InitPromise> VideoDecoderChild::Init() {
AssertOnManagerThread();
if (!mIPDLSelfRef) {
return MediaDataDecoder::InitPromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
}
// If we failed to send this, then we'll still resolve the Init promise
// as ActorDestroy handles it.
if (mCanSend) {
SendInit();
}
return mInitPromise.Ensure(__func__);
}
RefPtr<MediaDataDecoder::DecodePromise> VideoDecoderChild::Decode(
MediaRawData* aSample) {
AssertOnManagerThread();
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mGPUCrashTime);
return MediaDataDecoder::DecodePromise::CreateAndReject(error, __func__);
}
if (!mCanSend) {
// We're here if the IPC channel has died but we're still waiting for the
// RunWhenRecreated task to complete. The decode promise will be rejected
// when that task is run.
return mDecodePromise.Ensure(__func__);
}
// TODO: It would be nice to add an allocator method to
// MediaDataDecoder so that the demuxer could write directly
// into shmem rather than requiring a copy here.
Shmem buffer;
if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
return MediaDataDecoder::DecodePromise::CreateAndReject(
NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
}
memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
MediaRawDataIPDL sample(
MediaDataIPDL(aSample->mOffset, aSample->mTime, aSample->mTimecode,
aSample->mDuration, aSample->mKeyframe),
aSample->mEOS, aSample->mDiscardPadding, std::move(buffer));
SendInput(sample);
return mDecodePromise.Ensure(__func__);
}
RefPtr<MediaDataDecoder::FlushPromise> VideoDecoderChild::Flush() {
AssertOnManagerThread();
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mGPUCrashTime);
return MediaDataDecoder::FlushPromise::CreateAndReject(error, __func__);
}
if (mCanSend) {
SendFlush();
}
return mFlushPromise.Ensure(__func__);
}
RefPtr<MediaDataDecoder::DecodePromise> VideoDecoderChild::Drain() {
AssertOnManagerThread();
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mGPUCrashTime);
return MediaDataDecoder::DecodePromise::CreateAndReject(error, __func__);
}
if (mCanSend) {
SendDrain();
}
return mDrainPromise.Ensure(__func__);
}
RefPtr<ShutdownPromise> VideoDecoderChild::Shutdown() {
AssertOnManagerThread();
mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
mInitialized = false;
if (mNeedNewDecoder) {
MediaResult error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
error.SetGPUCrashTimeStamp(mGPUCrashTime);
return ShutdownPromise::CreateAndResolve(true, __func__);
}
if (!mCanSend) {
return ShutdownPromise::CreateAndResolve(true, __func__);
}
SendShutdown();
MOZ_ASSERT(!mShutdownSelfRef);
mShutdownSelfRef = this;
return mShutdownPromise.Ensure(__func__);
}
bool VideoDecoderChild::IsHardwareAccelerated(
nsACString& aFailureReason) const {
AssertOnManagerThread();
aFailureReason = mHardwareAcceleratedReason;
return mIsHardwareAccelerated;
}
nsCString VideoDecoderChild::GetDescriptionName() const {
AssertOnManagerThread();
return mDescription;
}
void VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime) {
AssertOnManagerThread();
if (mCanSend) {
SendSetSeekThreshold(aTime);
}
}
MediaDataDecoder::ConversionRequired VideoDecoderChild::NeedsConversion()
const {
AssertOnManagerThread();
return mConversion;
}
void VideoDecoderChild::AssertOnManagerThread() const {
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
}
VideoDecoderManagerChild* VideoDecoderChild::GetManager() {
if (!mCanSend) {
return nullptr;
}
return static_cast<VideoDecoderManagerChild*>(Manager());
}
} // namespace mozilla

Просмотреть файл

@ -1,101 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef include_ipc_VideoDecoderChild_h
#define include_ipc_VideoDecoderChild_h
#include "MediaResult.h"
#include "PlatformDecoderModule.h"
#include "mozilla/PVideoDecoderChild.h"
#include "IRemoteDecoderChild.h"
namespace mozilla {
class RemoteVideoDecoder;
class RemoteDecoderModule;
class VideoDecoderManagerChild;
using mozilla::ipc::IPCResult;
class VideoDecoderChild final : public PVideoDecoderChild,
public IRemoteDecoderChild {
friend class PVideoDecoderChild;
public:
explicit VideoDecoderChild();
// PVideoDecoderChild
IPCResult RecvOutput(const VideoDataIPDL& aData);
IPCResult RecvInputExhausted();
IPCResult RecvDrainComplete();
IPCResult RecvError(const nsresult& aError);
IPCResult RecvInitComplete(const nsCString& aDecoderDescription,
const bool& aHardware,
const nsCString& aHardwareReason,
const uint32_t& aConversion);
IPCResult RecvInitFailed(const nsresult& aReason);
IPCResult RecvFlushComplete();
IPCResult RecvShutdownComplete();
void ActorDestroy(ActorDestroyReason aWhy) override;
RefPtr<MediaDataDecoder::InitPromise> Init() override;
RefPtr<MediaDataDecoder::DecodePromise> Decode(
MediaRawData* aSample) override;
RefPtr<MediaDataDecoder::DecodePromise> Drain() override;
RefPtr<MediaDataDecoder::FlushPromise> Flush() override;
RefPtr<ShutdownPromise> Shutdown() override;
bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
nsCString GetDescriptionName() const override;
void SetSeekThreshold(const media::TimeUnit& aTime) override;
MediaDataDecoder::ConversionRequired NeedsConversion() const override;
void DestroyIPDL() override;
MOZ_IS_CLASS_INIT
MediaResult InitIPDL(const VideoInfo& aVideoInfo, float aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier);
// Called from IPDL when our actor has been destroyed
void IPDLActorDestroyed();
VideoDecoderManagerChild* GetManager();
private:
~VideoDecoderChild();
void AssertOnManagerThread() const;
RefPtr<VideoDecoderChild> mIPDLSelfRef;
RefPtr<nsIThread> mThread;
MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
MozPromiseHolder<MediaDataDecoder::DecodePromise> mDrainPromise;
MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushPromise;
MozPromiseHolder<ShutdownPromise> mShutdownPromise;
nsCString mHardwareAcceleratedReason;
nsCString mDescription;
bool mCanSend;
bool mInitialized;
bool mIsHardwareAccelerated;
MediaDataDecoder::ConversionRequired mConversion;
// Set to true if the actor got destroyed and we haven't yet notified the
// caller.
bool mNeedNewDecoder;
MediaDataDecoder::DecodedData mDecodedData;
nsCString mBlacklistedD3D11Driver;
nsCString mBlacklistedD3D9Driver;
TimeStamp mGPUCrashTime;
// Keep this instance alive during SendShutdown RecvShutdownComplete
// handshake.
RefPtr<VideoDecoderChild> mShutdownSelfRef;
};
} // namespace mozilla
#endif // include_ipc_VideoDecoderChild_h

Просмотреть файл

@ -1,258 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VideoDecoderManagerChild.h"
#include "VideoDecoderChild.h"
#include "nsThreadUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/layers/SynchronousTask.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/layers/ISurfaceAllocator.h"
#include "base/task.h"
namespace mozilla {
using namespace ipc;
using namespace layers;
using namespace gfx;
// Only modified on the main-thread
StaticRefPtr<nsIThread> sVideoDecoderChildThread;
StaticRefPtr<AbstractThread> sVideoDecoderChildAbstractThread;
// Only accessed from sVideoDecoderChildThread
static StaticRefPtr<VideoDecoderManagerChild> sDecoderManager;
static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks;
/* static */
void VideoDecoderManagerChild::InitializeThread() {
MOZ_ASSERT(NS_IsMainThread());
if (!sVideoDecoderChildThread) {
RefPtr<nsIThread> childThread;
nsresult rv = NS_NewNamedThread("VideoChild", getter_AddRefs(childThread));
NS_ENSURE_SUCCESS_VOID(rv);
sVideoDecoderChildThread = childThread;
sVideoDecoderChildAbstractThread =
AbstractThread::CreateXPCOMThreadWrapper(childThread, false);
sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>();
}
}
/* static */
void VideoDecoderManagerChild::InitForContent(
Endpoint<PVideoDecoderManagerChild>&& aVideoManager) {
InitializeThread();
sVideoDecoderChildThread->Dispatch(
NewRunnableFunction("InitForContentRunnable", &Open,
std::move(aVideoManager)),
NS_DISPATCH_NORMAL);
}
/* static */
void VideoDecoderManagerChild::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
if (sVideoDecoderChildThread) {
sVideoDecoderChildThread->Dispatch(
NS_NewRunnableFunction("VideoDecoderManagerChild::Shutdown",
[]() {
if (sDecoderManager &&
sDecoderManager->CanSend()) {
sDecoderManager->Close();
sDecoderManager = nullptr;
}
}),
NS_DISPATCH_NORMAL);
sVideoDecoderChildAbstractThread = nullptr;
sVideoDecoderChildThread->Shutdown();
sVideoDecoderChildThread = nullptr;
sRecreateTasks = nullptr;
}
}
void VideoDecoderManagerChild::RunWhenRecreated(
already_AddRefed<Runnable> aTask) {
MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
// If we've already been recreated, then run the task immediately.
if (sDecoderManager && sDecoderManager != this &&
sDecoderManager->CanSend()) {
RefPtr<Runnable> task = aTask;
task->Run();
} else {
sRecreateTasks->AppendElement(aTask);
}
}
/* static */
VideoDecoderManagerChild* VideoDecoderManagerChild::GetSingleton() {
MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
return sDecoderManager;
}
/* static */
nsIThread* VideoDecoderManagerChild::GetManagerThread() {
return sVideoDecoderChildThread;
}
/* static */
AbstractThread* VideoDecoderManagerChild::GetManagerAbstractThread() {
return sVideoDecoderChildAbstractThread;
}
PVideoDecoderChild* VideoDecoderManagerChild::AllocPVideoDecoderChild(
const VideoInfo& aVideoInfo, const float& aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* /* not used */, nsCString* /* not used */,
nsCString* /* not used */) {
return new VideoDecoderChild();
}
bool VideoDecoderManagerChild::DeallocPVideoDecoderChild(
PVideoDecoderChild* actor) {
VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor);
child->IPDLActorDestroyed();
return true;
}
void VideoDecoderManagerChild::Open(
Endpoint<PVideoDecoderManagerChild>&& aEndpoint) {
// Make sure we always dispatch everything in sRecreateTasks, even if we
// fail since this is as close to being recreated as we will ever be.
sDecoderManager = nullptr;
if (aEndpoint.IsValid()) {
RefPtr<VideoDecoderManagerChild> manager = new VideoDecoderManagerChild();
if (aEndpoint.Bind(manager)) {
sDecoderManager = manager;
manager->InitIPDL();
}
}
for (Runnable* task : *sRecreateTasks) {
task->Run();
}
sRecreateTasks->Clear();
}
void VideoDecoderManagerChild::InitIPDL() {
mCanSend = true;
mIPDLSelfRef = this;
}
void VideoDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy) {
mCanSend = false;
}
void VideoDecoderManagerChild::ActorDealloc() { mIPDLSelfRef = nullptr; }
bool VideoDecoderManagerChild::CanSend() {
MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
return mCanSend;
}
bool VideoDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem) {
if (NS_GetCurrentThread() != sVideoDecoderChildThread) {
RefPtr<VideoDecoderManagerChild> self = this;
mozilla::ipc::Shmem shmem = aShmem;
sVideoDecoderChildThread->Dispatch(
NS_NewRunnableFunction("VideoDecoderManagerChild::DeallocShmem",
[self, shmem]() {
if (self->CanSend()) {
mozilla::ipc::Shmem shmemCopy = shmem;
self->DeallocShmem(shmemCopy);
}
}),
NS_DISPATCH_NORMAL);
return true;
}
return PVideoDecoderManagerChild::DeallocShmem(aShmem);
}
struct SurfaceDescriptorUserData {
SurfaceDescriptorUserData(VideoDecoderManagerChild* aAllocator,
SurfaceDescriptor& aSD)
: mAllocator(aAllocator), mSD(aSD) {}
~SurfaceDescriptorUserData() { DestroySurfaceDescriptor(mAllocator, &mSD); }
RefPtr<VideoDecoderManagerChild> mAllocator;
SurfaceDescriptor mSD;
};
void DeleteSurfaceDescriptorUserData(void* aClosure) {
SurfaceDescriptorUserData* sd =
reinterpret_cast<SurfaceDescriptorUserData*>(aClosure);
delete sd;
}
already_AddRefed<SourceSurface> VideoDecoderManagerChild::Readback(
const SurfaceDescriptorGPUVideo& aSD) {
// We can't use NS_DISPATCH_SYNC here since that can spin the event
// loop while it waits. This function can be called from JS and we
// don't want that to happen.
SynchronousTask task("Readback sync");
RefPtr<VideoDecoderManagerChild> ref = this;
SurfaceDescriptor sd;
if (NS_FAILED(sVideoDecoderChildThread->Dispatch(
NS_NewRunnableFunction("VideoDecoderManagerChild::Readback",
[&]() {
AutoCompleteTask complete(&task);
if (ref->CanSend()) {
ref->SendReadback(aSD, &sd);
}
}),
NS_DISPATCH_NORMAL))) {
return nullptr;
}
task.Wait();
if (!IsSurfaceDescriptorValid(sd)) {
return nullptr;
}
RefPtr<DataSourceSurface> source = GetSurfaceForDescriptor(sd);
if (!source) {
DestroySurfaceDescriptor(this, &sd);
NS_WARNING("Failed to map SurfaceDescriptor in Readback");
return nullptr;
}
static UserDataKey sSurfaceDescriptor;
source->AddUserData(&sSurfaceDescriptor,
new SurfaceDescriptorUserData(this, sd),
DeleteSurfaceDescriptorUserData);
return source.forget();
}
void VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD) {
RefPtr<VideoDecoderManagerChild> ref = this;
SurfaceDescriptorGPUVideo sd = std::move(aSD);
sVideoDecoderChildThread->Dispatch(
NS_NewRunnableFunction(
"VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo",
[ref, sd]() {
if (ref->CanSend()) {
ref->SendDeallocateSurfaceDescriptorGPUVideo(sd);
}
}),
NS_DISPATCH_NORMAL);
}
void VideoDecoderManagerChild::HandleFatalError(const char* aMsg) const {
dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
}
} // namespace mozilla

Просмотреть файл

@ -1,99 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef include_ipc_VideoDecoderManagerChild_h
#define include_ipc_VideoDecoderManagerChild_h
#include "mozilla/RefPtr.h"
#include "mozilla/PVideoDecoderManagerChild.h"
namespace mozilla {
namespace gfx {
class SourceSurface;
}
class VideoDecoderManagerChild final : public PVideoDecoderManagerChild,
public mozilla::ipc::IShmemAllocator {
friend class PVideoDecoderManagerChild;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerChild)
// Can only be called from the manager thread
static VideoDecoderManagerChild* GetSingleton();
// Can be called from any thread.
static nsIThread* GetManagerThread();
static AbstractThread* GetManagerAbstractThread();
// Can be called from any thread, dispatches the request to the IPDL thread
// internally and will be ignored if the IPDL actor has been destroyed.
already_AddRefed<gfx::SourceSurface> Readback(
const SurfaceDescriptorGPUVideo& aSD);
void DeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD);
bool AllocShmem(size_t aSize,
mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
mozilla::ipc::Shmem* aShmem) override {
return PVideoDecoderManagerChild::AllocShmem(aSize, aShmType, aShmem);
}
bool AllocUnsafeShmem(size_t aSize,
mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
mozilla::ipc::Shmem* aShmem) override {
return PVideoDecoderManagerChild::AllocUnsafeShmem(aSize, aShmType, aShmem);
}
// Can be called from any thread, dispatches the request to the IPDL thread
// internally and will be ignored if the IPDL actor has been destroyed.
bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
// Main thread only
static void InitForContent(
Endpoint<PVideoDecoderManagerChild>&& aVideoManager);
static void Shutdown();
// Run aTask (on the manager thread) when we next attempt to create a new
// manager (even if creation fails). Intended to be called from ActorDestroy
// when we get notified that the old manager is being destroyed. Can only be
// called from the manager thread.
void RunWhenRecreated(already_AddRefed<Runnable> aTask);
bool CanSend();
protected:
void InitIPDL();
void ActorDestroy(ActorDestroyReason aWhy) override;
void ActorDealloc() override;
void HandleFatalError(const char* aMsg) const override;
PVideoDecoderChild* AllocPVideoDecoderChild(
const VideoInfo& aVideoInfo, const float& aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* aBlacklistedD3D11Driver, nsCString* aBlacklistedD3D9Driver,
nsCString* aErrorDescription);
bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor);
private:
// Main thread only
static void InitializeThread();
VideoDecoderManagerChild() : mCanSend(false) {}
~VideoDecoderManagerChild() {}
static void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint);
RefPtr<VideoDecoderManagerChild> mIPDLSelfRef;
// Should only ever be accessed on the manager thread.
bool mCanSend;
};
} // namespace mozilla
#endif // include_ipc_VideoDecoderManagerChild_h

Просмотреть файл

@ -1,278 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VideoDecoderManagerParent.h"
#include "VideoDecoderParent.h"
#include "VideoUtils.h"
#include "base/thread.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Services.h"
#include "mozilla/Observer.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "nsIEventTarget.h"
#include "nsThreadUtils.h"
#include "ImageContainer.h"
#include "mozilla/layers/VideoBridgeChild.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/SyncRunnable.h"
#if XP_WIN
# include <objbase.h>
#endif
namespace mozilla {
#ifdef XP_WIN
extern const nsCString GetFoundD3D11BlacklistedDLL();
extern const nsCString GetFoundD3D9BlacklistedDLL();
#endif // XP_WIN
using namespace ipc;
using namespace layers;
using namespace gfx;
SurfaceDescriptorGPUVideo VideoDecoderManagerParent::StoreImage(
Image* aImage, TextureClient* aTexture) {
SurfaceDescriptorGPUVideo ret;
aTexture->GPUVideoDesc(&ret);
mImageMap[ret.handle()] = aImage;
mTextureMap[ret.handle()] = aTexture;
return ret;
}
StaticRefPtr<nsIThread> sVideoDecoderManagerThread;
StaticRefPtr<TaskQueue> sManagerTaskQueue;
class VideoDecoderManagerThreadHolder {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerThreadHolder)
public:
VideoDecoderManagerThreadHolder() {}
private:
~VideoDecoderManagerThreadHolder() {
NS_DispatchToMainThread(
NS_NewRunnableFunction("VideoDecoderManagerThreadHolder::~"
"VideoDecoderManagerThreadHolder",
[]() -> void {
sVideoDecoderManagerThread->Shutdown();
sVideoDecoderManagerThread = nullptr;
}));
}
};
StaticRefPtr<VideoDecoderManagerThreadHolder> sVideoDecoderManagerThreadHolder;
class ManagerThreadShutdownObserver : public nsIObserver {
virtual ~ManagerThreadShutdownObserver() = default;
public:
ManagerThreadShutdownObserver() {}
NS_DECL_ISUPPORTS
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) override {
MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
VideoDecoderManagerParent::ShutdownThreads();
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(ManagerThreadShutdownObserver, nsIObserver);
void VideoDecoderManagerParent::StartupThreads() {
MOZ_ASSERT(NS_IsMainThread());
if (sVideoDecoderManagerThread) {
return;
}
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (!observerService) {
return;
}
RefPtr<nsIThread> managerThread;
nsresult rv = NS_NewNamedThread("VideoParent", getter_AddRefs(managerThread));
if (NS_FAILED(rv)) {
return;
}
sVideoDecoderManagerThread = managerThread;
sVideoDecoderManagerThreadHolder = new VideoDecoderManagerThreadHolder();
#if XP_WIN
sVideoDecoderManagerThread->Dispatch(
NS_NewRunnableFunction("VideoDecoderManagerParent::StartupThreads",
[]() {
DebugOnly<HRESULT> hr =
CoInitializeEx(0, COINIT_MULTITHREADED);
MOZ_ASSERT(hr == S_OK);
}),
NS_DISPATCH_NORMAL);
#endif
sVideoDecoderManagerThread->Dispatch(
NS_NewRunnableFunction("VideoDecoderManagerParent::StartupThreads",
[]() { layers::VideoBridgeChild::Startup(); }),
NS_DISPATCH_NORMAL);
sManagerTaskQueue = new TaskQueue(
managerThread.forget(), "VideoDecoderManagerParent::sManagerTaskQueue");
auto* obs = new ManagerThreadShutdownObserver();
observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
}
void VideoDecoderManagerParent::ShutdownThreads() {
sManagerTaskQueue->BeginShutdown();
sManagerTaskQueue->AwaitShutdownAndIdle();
sManagerTaskQueue = nullptr;
sVideoDecoderManagerThreadHolder = nullptr;
while (sVideoDecoderManagerThread) {
NS_ProcessNextEvent(nullptr, true);
}
}
void VideoDecoderManagerParent::ShutdownVideoBridge() {
if (sVideoDecoderManagerThread) {
RefPtr<Runnable> task =
NS_NewRunnableFunction("VideoDecoderManagerParent::ShutdownVideoBridge",
[]() { VideoBridgeChild::Shutdown(); });
SyncRunnable::DispatchToThread(sVideoDecoderManagerThread, task);
}
}
bool VideoDecoderManagerParent::OnManagerThread() {
return NS_GetCurrentThread() == sVideoDecoderManagerThread;
}
bool VideoDecoderManagerParent::CreateForContent(
Endpoint<PVideoDecoderManagerParent>&& aEndpoint) {
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
MOZ_ASSERT(NS_IsMainThread());
StartupThreads();
if (!sVideoDecoderManagerThread) {
return false;
}
RefPtr<VideoDecoderManagerParent> parent =
new VideoDecoderManagerParent(sVideoDecoderManagerThreadHolder);
RefPtr<Runnable> task =
NewRunnableMethod<Endpoint<PVideoDecoderManagerParent>&&>(
"VideoDecoderManagerParent::Open", parent,
&VideoDecoderManagerParent::Open, std::move(aEndpoint));
sVideoDecoderManagerThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
return true;
}
VideoDecoderManagerParent::VideoDecoderManagerParent(
VideoDecoderManagerThreadHolder* aHolder)
: mThreadHolder(aHolder) {
MOZ_COUNT_CTOR(VideoDecoderManagerParent);
}
VideoDecoderManagerParent::~VideoDecoderManagerParent() {
MOZ_COUNT_DTOR(VideoDecoderManagerParent);
}
void VideoDecoderManagerParent::ActorDestroy(
mozilla::ipc::IProtocol::ActorDestroyReason) {
mThreadHolder = nullptr;
}
PVideoDecoderParent* VideoDecoderManagerParent::AllocPVideoDecoderParent(
const VideoInfo& aVideoInfo, const float& aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* aBlacklistedD3D11Driver, nsCString* aBlacklistedD3D9Driver,
nsCString* aErrorDescription) {
RefPtr<TaskQueue> decodeTaskQueue =
new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
"VideoDecoderParent::mDecodeTaskQueue");
auto* parent = new VideoDecoderParent(
this, aVideoInfo, aFramerate, aOptions, aIdentifier, sManagerTaskQueue,
decodeTaskQueue, aSuccess, aErrorDescription);
#ifdef XP_WIN
*aBlacklistedD3D11Driver = GetFoundD3D11BlacklistedDLL();
*aBlacklistedD3D9Driver = GetFoundD3D9BlacklistedDLL();
#endif // XP_WIN
return parent;
}
bool VideoDecoderManagerParent::DeallocPVideoDecoderParent(
PVideoDecoderParent* actor) {
VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor);
parent->Destroy();
return true;
}
void VideoDecoderManagerParent::Open(
Endpoint<PVideoDecoderManagerParent>&& aEndpoint) {
if (!aEndpoint.Bind(this)) {
// We can't recover from this.
MOZ_CRASH("Failed to bind VideoDecoderManagerParent to endpoint");
}
AddRef();
}
void VideoDecoderManagerParent::ActorDealloc() { Release(); }
mozilla::ipc::IPCResult VideoDecoderManagerParent::RecvReadback(
const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) {
RefPtr<Image> image = mImageMap[aSD.handle()];
if (!image) {
*aResult = null_t();
return IPC_OK();
}
RefPtr<SourceSurface> source = image->GetAsSourceSurface();
if (!source) {
*aResult = null_t();
return IPC_OK();
}
SurfaceFormat format = source->GetFormat();
IntSize size = source->GetSize();
size_t length = ImageDataSerializer::ComputeRGBBufferSize(size, format);
Shmem buffer;
if (!length ||
!AllocShmem(length, Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
*aResult = null_t();
return IPC_OK();
}
RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
gfx::BackendType::CAIRO, buffer.get<uint8_t>(), size,
ImageDataSerializer::ComputeRGBStride(format, size.width), format);
if (!dt) {
DeallocShmem(buffer);
*aResult = null_t();
return IPC_OK();
}
dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
dt->Flush();
*aResult = SurfaceDescriptorBuffer(RGBDescriptor(size, format, true),
MemoryOrShmem(std::move(buffer)));
return IPC_OK();
}
mozilla::ipc::IPCResult
VideoDecoderManagerParent::RecvDeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD) {
mImageMap.erase(aSD.handle());
mTextureMap.erase(aSD.handle());
return IPC_OK();
}
} // namespace mozilla

Просмотреть файл

@ -1,68 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef include_ipc_VideoDecoderManagerParent_h
#define include_ipc_VideoDecoderManagerParent_h
#include "mozilla/PVideoDecoderManagerParent.h"
namespace mozilla {
class VideoDecoderManagerThreadHolder;
class VideoDecoderManagerParent final : public PVideoDecoderManagerParent {
friend class PVideoDecoderManagerParent;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerParent)
static bool CreateForContent(
Endpoint<PVideoDecoderManagerParent>&& aEndpoint);
// Can be called from any thread
SurfaceDescriptorGPUVideo StoreImage(layers::Image* aImage,
layers::TextureClient* aTexture);
static void StartupThreads();
static void ShutdownThreads();
static void ShutdownVideoBridge();
bool OnManagerThread();
protected:
PVideoDecoderParent* AllocPVideoDecoderParent(
const VideoInfo& aVideoInfo, const float& aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess,
nsCString* aBlacklistedD3D11Driver, nsCString* aBlacklistedD3D9Driver,
nsCString* aErrorDescription);
bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor);
mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD,
SurfaceDescriptor* aResult);
mozilla::ipc::IPCResult RecvDeallocateSurfaceDescriptorGPUVideo(
const SurfaceDescriptorGPUVideo& aSD);
void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
void ActorDealloc() override;
private:
explicit VideoDecoderManagerParent(
VideoDecoderManagerThreadHolder* aThreadHolder);
~VideoDecoderManagerParent();
void Open(Endpoint<PVideoDecoderManagerParent>&& aEndpoint);
std::map<uint64_t, RefPtr<layers::Image>> mImageMap;
std::map<uint64_t, RefPtr<layers::TextureClient>> mTextureMap;
RefPtr<VideoDecoderManagerThreadHolder> mThreadHolder;
};
} // namespace mozilla
#endif // include_dom_ipc_VideoDecoderManagerParent_h

Просмотреть файл

@ -1,289 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "VideoDecoderParent.h"
#include "mozilla/Unused.h"
#include "mozilla/layers/CompositorThread.h"
#include "base/thread.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/VideoBridgeChild.h"
#include "mozilla/layers/ImageClient.h"
#include "MediaInfo.h"
#include "PDMFactory.h"
#include "VideoDecoderManagerParent.h"
#ifdef XP_WIN
# include "WMFDecoderModule.h"
#endif
namespace mozilla {
using base::Thread;
using media::TimeUnit;
using namespace ipc;
using namespace layers;
using namespace gfx;
class KnowsCompositorVideo : public layers::KnowsCompositor {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KnowsCompositorVideo, override)
layers::TextureForwarder* GetTextureForwarder() override {
return VideoBridgeChild::GetSingleton();
}
layers::LayersIPCActor* GetLayersIPCActor() override {
return VideoBridgeChild::GetSingleton();
}
private:
virtual ~KnowsCompositorVideo() = default;
};
VideoDecoderParent::VideoDecoderParent(
VideoDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier,
TaskQueue* aManagerTaskQueue, TaskQueue* aDecodeTaskQueue, bool* aSuccess,
nsCString* aErrorDescription)
: mParent(aParent),
mManagerTaskQueue(aManagerTaskQueue),
mDecodeTaskQueue(aDecodeTaskQueue),
mKnowsCompositor(new KnowsCompositorVideo),
mDestroyed(false) {
MOZ_COUNT_CTOR(VideoDecoderParent);
MOZ_ASSERT(OnManagerThread());
// We hold a reference to ourselves to keep us alive until IPDL
// explictly destroys us. There may still be refs held by
// tasks, but no new ones should be added after we're
// destroyed.
mIPDLSelfRef = this;
mKnowsCompositor->IdentifyTextureHost(aIdentifier);
#ifdef XP_WIN
using Option = CreateDecoderParams::Option;
using OptionSet = CreateDecoderParams::OptionSet;
// Ensure everything is properly initialized on the right thread.
PDMFactory::EnsureInit();
// TODO: Ideally we wouldn't hardcode the WMF PDM, and we'd use the normal PDM
// factory logic for picking a decoder.
RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
pdm->Startup();
CreateDecoderParams params(aVideoInfo);
params.mTaskQueue = mDecodeTaskQueue;
params.mKnowsCompositor = mKnowsCompositor;
params.mImageContainer = new layers::ImageContainer();
params.mRate = CreateDecoderParams::VideoFrameRate(aFramerate);
params.mOptions = aOptions;
MediaResult error(NS_OK);
params.mError = &error;
mDecoder = pdm->CreateVideoDecoder(params);
if (NS_FAILED(error)) {
MOZ_ASSERT(aErrorDescription);
*aErrorDescription = error.Description();
}
#else
MOZ_ASSERT(false,
"Can't use RemoteVideoDecoder on non-Windows platforms yet");
#endif
*aSuccess = !!mDecoder;
}
VideoDecoderParent::~VideoDecoderParent() {
MOZ_COUNT_DTOR(VideoDecoderParent);
}
void VideoDecoderParent::Destroy() {
MOZ_ASSERT(OnManagerThread());
mDestroyed = true;
mIPDLSelfRef = nullptr;
}
mozilla::ipc::IPCResult VideoDecoderParent::RecvInit() {
MOZ_ASSERT(OnManagerThread());
RefPtr<VideoDecoderParent> self = this;
mDecoder->Init()->Then(
mManagerTaskQueue, __func__,
[self](TrackInfo::TrackType aTrack) {
if (self->mDecoder) {
nsCString hardwareReason;
bool hardwareAccelerated =
self->mDecoder->IsHardwareAccelerated(hardwareReason);
uint32_t conversion =
static_cast<uint32_t>(self->mDecoder->NeedsConversion());
Unused << self->SendInitComplete(self->mDecoder->GetDescriptionName(),
hardwareAccelerated, hardwareReason,
conversion);
}
},
[self](MediaResult aReason) {
if (!self->mDestroyed) {
Unused << self->SendInitFailed(aReason);
}
});
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderParent::RecvInput(
const MediaRawDataIPDL& aData) {
MOZ_ASSERT(OnManagerThread());
// XXX: This copies the data into a buffer owned by the MediaRawData. Ideally
// we'd just take ownership of the shmem.
RefPtr<MediaRawData> data = new MediaRawData(aData.buffer().get<uint8_t>(),
aData.buffer().Size<uint8_t>());
if (aData.buffer().Size<uint8_t>() && !data->Data()) {
// OOM
Error(NS_ERROR_OUT_OF_MEMORY);
return IPC_OK();
}
data->mOffset = aData.base().offset();
data->mTime = aData.base().time();
data->mTimecode = aData.base().timecode();
data->mDuration = aData.base().duration();
data->mKeyframe = aData.base().keyframe();
data->mEOS = aData.eos();
data->mDiscardPadding = aData.discardPadding();
DeallocShmem(aData.buffer());
RefPtr<VideoDecoderParent> self = this;
mDecoder->Decode(data)->Then(
mManagerTaskQueue, __func__,
[self, this](MediaDataDecoder::DecodedData&& aResults) {
if (mDestroyed) {
return;
}
ProcessDecodedData(std::move(aResults));
Unused << SendInputExhausted();
},
[self](const MediaResult& aError) { self->Error(aError); });
return IPC_OK();
}
void VideoDecoderParent::ProcessDecodedData(
MediaDataDecoder::DecodedData&& aData) {
MOZ_ASSERT(OnManagerThread());
// If the video decoder bridge has shut down, stop.
if (!mKnowsCompositor->GetTextureForwarder()) {
return;
}
for (auto&& data : aData) {
MOZ_ASSERT(data->mType == MediaData::Type::VIDEO_DATA,
"Can only decode videos using VideoDecoderParent!");
VideoData* video = static_cast<VideoData*>(data.get());
MOZ_ASSERT(video->mImage,
"Decoded video must output a layer::Image to "
"be used with VideoDecoderParent");
RefPtr<TextureClient> texture =
video->mImage->GetTextureClient(mKnowsCompositor);
if (!texture) {
texture = ImageClient::CreateTextureClientForImage(video->mImage,
mKnowsCompositor);
}
if (texture && !texture->IsAddedToCompositableClient()) {
texture->InitIPDLActor(mKnowsCompositor);
texture->SetAddedToCompositableClient();
}
VideoDataIPDL output(
MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
data->mDuration, data->mKeyframe),
video->mDisplay, texture ? texture->GetSize() : IntSize(),
texture ? mParent->StoreImage(video->mImage, texture)
: SurfaceDescriptorGPUVideo(0, null_t()),
video->mFrameID);
Unused << SendOutput(output);
}
}
mozilla::ipc::IPCResult VideoDecoderParent::RecvFlush() {
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(OnManagerThread());
RefPtr<VideoDecoderParent> self = this;
mDecoder->Flush()->Then(
mManagerTaskQueue, __func__,
[self]() {
if (!self->mDestroyed) {
Unused << self->SendFlushComplete();
}
},
[self](const MediaResult& aError) { self->Error(aError); });
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderParent::RecvDrain() {
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(OnManagerThread());
RefPtr<VideoDecoderParent> self = this;
mDecoder->Drain()->Then(
mManagerTaskQueue, __func__,
[self, this](MediaDataDecoder::DecodedData&& aResults) {
if (!mDestroyed) {
ProcessDecodedData(std::move(aResults));
Unused << SendDrainComplete();
}
},
[self](const MediaResult& aError) { self->Error(aError); });
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderParent::RecvShutdown() {
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(OnManagerThread());
if (mDecoder) {
RefPtr<VideoDecoderParent> self = this;
mDecoder->Shutdown()->Then(
mManagerTaskQueue, __func__,
[self](const ShutdownPromise::ResolveOrRejectValue& aValue) {
MOZ_ASSERT(aValue.IsResolve());
if (!self->mDestroyed) {
Unused << self->SendShutdownComplete();
}
});
}
mDecoder = nullptr;
return IPC_OK();
}
mozilla::ipc::IPCResult VideoDecoderParent::RecvSetSeekThreshold(
const TimeUnit& aTime) {
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(OnManagerThread());
mDecoder->SetSeekThreshold(aTime);
return IPC_OK();
}
void VideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy) {
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(OnManagerThread());
if (mDecoder) {
mDecoder->Shutdown();
mDecoder = nullptr;
}
}
void VideoDecoderParent::Error(const MediaResult& aError) {
MOZ_ASSERT(OnManagerThread());
if (!mDestroyed) {
Unused << SendError(aError);
}
}
bool VideoDecoderParent::OnManagerThread() {
return mParent->OnManagerThread();
}
} // namespace mozilla

Просмотреть файл

@ -1,69 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef include_ipc_VideoDecoderParent_h
#define include_ipc_VideoDecoderParent_h
#include "ImageContainer.h"
#include "MediaData.h"
#include "PlatformDecoderModule.h"
#include "VideoDecoderManagerParent.h"
#include "mozilla/MozPromise.h"
#include "mozilla/PVideoDecoderParent.h"
#include "mozilla/layers/TextureForwarder.h"
namespace mozilla {
class KnowsCompositorVideo;
using mozilla::ipc::IPCResult;
class VideoDecoderParent final : public PVideoDecoderParent {
friend class PVideoDecoderParent;
public:
// We refcount this class since the task queue can have runnables
// that reference us.
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderParent)
VideoDecoderParent(VideoDecoderManagerParent* aParent,
const VideoInfo& aVideoInfo, float aFramerate,
const CreateDecoderParams::OptionSet& aOptions,
const layers::TextureFactoryIdentifier& aIdentifier,
TaskQueue* aManagerTaskQueue, TaskQueue* aDecodeTaskQueue,
bool* aSuccess, nsCString* aErrorDescription);
void Destroy();
// PVideoDecoderParent
IPCResult RecvInit();
IPCResult RecvInput(const MediaRawDataIPDL& aData);
IPCResult RecvFlush();
IPCResult RecvDrain();
IPCResult RecvShutdown();
IPCResult RecvSetSeekThreshold(const media::TimeUnit& aTime);
void ActorDestroy(ActorDestroyReason aWhy) override;
private:
bool OnManagerThread();
void Error(const MediaResult& aError);
~VideoDecoderParent();
void ProcessDecodedData(MediaDataDecoder::DecodedData&& aData);
RefPtr<VideoDecoderManagerParent> mParent;
RefPtr<VideoDecoderParent> mIPDLSelfRef;
RefPtr<TaskQueue> mManagerTaskQueue;
RefPtr<TaskQueue> mDecodeTaskQueue;
RefPtr<MediaDataDecoder> mDecoder;
RefPtr<KnowsCompositorVideo> mKnowsCompositor;
// Can only be accessed from the manager thread
bool mDestroyed;
};
} // namespace mozilla
#endif // include_ipc_VideoDecoderParent_h

Просмотреть файл

@ -10,8 +10,6 @@ IPDL_SOURCES += [
'PRDD.ipdl',
'PRemoteDecoder.ipdl',
'PRemoteDecoderManager.ipdl',
'PVideoDecoder.ipdl',
'PVideoDecoderManager.ipdl',
]
EXPORTS.mozilla += [
@ -28,10 +26,6 @@ EXPORTS.mozilla += [
'RemoteDecoderModule.h',
'RemoteDecoderParent.h',
'RemoteMediaDataDecoder.h',
'VideoDecoderChild.h',
'VideoDecoderManagerChild.h',
'VideoDecoderManagerParent.h',
'VideoDecoderParent.h',
]
EXPORTS.mozilla.dom += [
@ -53,10 +47,6 @@ SOURCES += [
'RemoteDecoderParent.cpp',
'RemoteMediaDataDecoder.cpp',
'RemoteVideoDecoder.cpp',
'VideoDecoderChild.cpp',
'VideoDecoderManagerChild.cpp',
'VideoDecoderManagerParent.cpp',
'VideoDecoderParent.cpp',
]
# so we can include nsMacUtilsImpl.h in RDDParent.cpp for sandboxing

Просмотреть файл

@ -72,7 +72,7 @@ class PDMFactory final {
bool mFFmpegFailedToLoad = false;
bool mGMPPDMFailedToStartup = false;
friend class VideoDecoderParent;
friend class RemoteVideoDecoderParent;
static void EnsureInit();
template <class T>
friend class StaticAutoPtr;

Просмотреть файл

@ -17,8 +17,8 @@
#include "mozilla/StaticPrefs.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/VideoDecoderManagerChild.h"
#include "mozilla/VideoDecoderManagerParent.h"
#include "mozilla/RemoteDecoderManagerChild.h"
#include "mozilla/RemoteDecoderManagerParent.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/gfxVars.h"
@ -413,9 +413,9 @@ mozilla::ipc::IPCResult GPUParent::RecvNewContentVRManager(
return IPC_OK();
}
mozilla::ipc::IPCResult GPUParent::RecvNewContentVideoDecoderManager(
Endpoint<PVideoDecoderManagerParent>&& aEndpoint) {
if (!VideoDecoderManagerParent::CreateForContent(std::move(aEndpoint))) {
mozilla::ipc::IPCResult GPUParent::RecvNewContentRemoteDecoderManager(
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
if (!RemoteDecoderManagerParent::CreateForContent(std::move(aEndpoint))) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
@ -510,7 +510,7 @@ void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
mVsyncBridge->Shutdown();
mVsyncBridge = nullptr;
}
VideoDecoderManagerParent::ShutdownVideoBridge();
RemoteDecoderManagerParent::ShutdownVideoBridge();
CompositorThreadHolder::Shutdown();
// There is a case that RenderThread exists when gfxVars::UseWebRender() is
// false. This could happen when WebRender was fallbacked to compositor.

Просмотреть файл

@ -62,8 +62,8 @@ class GPUParent final : public PGPUParent {
Endpoint<PImageBridgeParent>&& aEndpoint);
mozilla::ipc::IPCResult RecvNewContentVRManager(
Endpoint<PVRManagerParent>&& aEndpoint);
mozilla::ipc::IPCResult RecvNewContentVideoDecoderManager(
Endpoint<PVideoDecoderManagerParent>&& aEndpoint);
mozilla::ipc::IPCResult RecvNewContentRemoteDecoderManager(
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint);
mozilla::ipc::IPCResult RecvGetDeviceStatus(GPUDeviceData* aOutStatus);
mozilla::ipc::IPCResult RecvSimulateDeviceReset(GPUDeviceData* aOutStatus);
mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping(

Просмотреть файл

@ -13,8 +13,8 @@
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/StaticPrefs.h"
#include "mozilla/VideoDecoderManagerChild.h"
#include "mozilla/VideoDecoderManagerParent.h"
#include "mozilla/RemoteDecoderManagerChild.h"
#include "mozilla/RemoteDecoderManagerParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/layers/APZCTreeManagerChild.h"
@ -799,7 +799,7 @@ bool GPUProcessManager::CreateContentBridges(
ipc::Endpoint<PCompositorManagerChild>* aOutCompositor,
ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
ipc::Endpoint<PVideoDecoderManagerChild>* aOutVideoManager,
ipc::Endpoint<PRemoteDecoderManagerChild>* aOutVideoManager,
nsTArray<uint32_t>* aNamespaces) {
if (!CreateContentCompositorManager(aOtherProcess, aOutCompositor) ||
!CreateContentImageBridge(aOtherProcess, aOutImageBridge) ||
@ -808,7 +808,7 @@ bool GPUProcessManager::CreateContentBridges(
}
// VideoDeocderManager is only supported in the GPU process, so we allow this
// to be fallible.
CreateContentVideoDecoderManager(aOtherProcess, aOutVideoManager);
CreateContentRemoteDecoderManager(aOtherProcess, aOutVideoManager);
// Allocates 3 namespaces(for CompositorManagerChild, CompositorBridgeChild
// and ImageBridgeChild)
aNamespaces->AppendElement(AllocateNamespace());
@ -910,18 +910,18 @@ bool GPUProcessManager::CreateContentVRManager(
return true;
}
void GPUProcessManager::CreateContentVideoDecoderManager(
void GPUProcessManager::CreateContentRemoteDecoderManager(
base::ProcessId aOtherProcess,
ipc::Endpoint<PVideoDecoderManagerChild>* aOutEndpoint) {
ipc::Endpoint<PRemoteDecoderManagerChild>* aOutEndpoint) {
if (!EnsureGPUReady() || !StaticPrefs::MediaGpuProcessDecoder() ||
!mDecodeVideoOnGpuProcess) {
return;
}
ipc::Endpoint<PVideoDecoderManagerParent> parentPipe;
ipc::Endpoint<PVideoDecoderManagerChild> childPipe;
ipc::Endpoint<PRemoteDecoderManagerParent> parentPipe;
ipc::Endpoint<PRemoteDecoderManagerChild> childPipe;
nsresult rv = PVideoDecoderManager::CreateEndpoints(
nsresult rv = PRemoteDecoderManager::CreateEndpoints(
mGPUChild->OtherPid(), aOtherProcess, &parentPipe, &childPipe);
if (NS_FAILED(rv)) {
gfxCriticalNote << "Could not create content video decoder: "
@ -929,7 +929,7 @@ void GPUProcessManager::CreateContentVideoDecoderManager(
return;
}
mGPUChild->SendNewContentVideoDecoderManager(std::move(parentPipe));
mGPUChild->SendNewContentRemoteDecoderManager(std::move(parentPipe));
*aOutEndpoint = std::move(childPipe);
}

Просмотреть файл

@ -24,7 +24,7 @@ class nsBaseWidget;
namespace mozilla {
class MemoryReportingProcess;
class PVideoDecoderManagerChild;
class PRemoteDecoderManagerChild;
namespace layers {
class IAPZCTreeManager;
class CompositorOptions;
@ -101,7 +101,7 @@ class GPUProcessManager final : public GPUProcessHost::Listener {
mozilla::ipc::Endpoint<PCompositorManagerChild>* aOutCompositor,
mozilla::ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
mozilla::ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
mozilla::ipc::Endpoint<PVideoDecoderManagerChild>* aOutVideoManager,
mozilla::ipc::Endpoint<PRemoteDecoderManagerChild>* aOutVideoManager,
nsTArray<uint32_t>* aNamespaces);
// Maps the layer tree and process together so that aOwningPID is allowed
@ -193,9 +193,9 @@ class GPUProcessManager final : public GPUProcessHost::Listener {
bool CreateContentVRManager(
base::ProcessId aOtherProcess,
mozilla::ipc::Endpoint<PVRManagerChild>* aOutEndpoint);
void CreateContentVideoDecoderManager(
void CreateContentRemoteDecoderManager(
base::ProcessId aOtherProcess,
mozilla::ipc::Endpoint<PVideoDecoderManagerChild>* aOutEndPoint);
mozilla::ipc::Endpoint<PRemoteDecoderManagerChild>* aOutEndPoint);
// Called from RemoteCompositorSession. We track remote sessions so we can
// notify their owning widgets that the session must be restarted.

Просмотреть файл

@ -15,7 +15,7 @@ include protocol PVRGPU;
include protocol PVRManager;
include protocol PVsyncBridge;
include protocol PUiCompositorController;
include protocol PVideoDecoderManager;
include protocol PRemoteDecoderManager;
using base::ProcessId from "base/process.h";
using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
@ -71,7 +71,7 @@ parent:
async NewContentCompositorManager(Endpoint<PCompositorManagerParent> endpoint);
async NewContentImageBridge(Endpoint<PImageBridgeParent> endpoint);
async NewContentVRManager(Endpoint<PVRManagerParent> endpoint);
async NewContentVideoDecoderManager(Endpoint<PVideoDecoderManagerParent> endpoint);
async NewContentRemoteDecoderManager(Endpoint<PRemoteDecoderManagerParent> endpoint);
// Called to notify the GPU process of who owns a layersId.
sync AddLayerTreeIdMapping(LayerTreeIdMapping mapping);

Просмотреть файл

@ -15,7 +15,7 @@
namespace mozilla {
namespace dom {
class VideoDecoderManagerChild;
class RemoteDecoderManagerChild;
}
namespace gl {
class GLBlitHelper;
@ -28,7 +28,7 @@ class GPUVideoImage final : public Image {
friend class gl::GLBlitHelper;
public:
GPUVideoImage(VideoDecoderManagerChild* aManager,
GPUVideoImage(RemoteDecoderManagerChild* aManager,
const SurfaceDescriptorGPUVideo& aSD, const gfx::IntSize& aSize)
: Image(nullptr, ImageFormat::GPU_VIDEO), mSize(aSize) {
// Create the TextureClient immediately since the GPUVideoTextureData

Просмотреть файл

@ -2572,9 +2572,9 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent,
// aEvent.mLocalStartPoint) would mess up velocity calculation. (This is
// the only caller of UpdateWithTouchAtDevicePoint() for pan events, so
// there is no risk of other calls resetting the position.)
mX.UpdateWithTouchAtDevicePoint(mX.GetPos() + logicalPanDisplacement.x,
mX.UpdateWithTouchAtDevicePoint(mX.GetPos() - logicalPanDisplacement.x,
aEvent.mTime);
mY.UpdateWithTouchAtDevicePoint(mY.GetPos() + logicalPanDisplacement.y,
mY.UpdateWithTouchAtDevicePoint(mY.GetPos() - logicalPanDisplacement.y,
aEvent.mTime);
HandlePanningUpdate(physicalPanDisplacement);

Просмотреть файл

@ -126,4 +126,19 @@ nsEventStatus MouseUp(const RefPtr<InputReceiver>& aTarget,
return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
}
template <class InputReceiver>
nsEventStatus PanGesture(PanGestureInput::PanGestureType aType,
const RefPtr<InputReceiver>& aTarget,
const ScreenIntPoint& aPoint,
const ScreenPoint& aDelta, TimeStamp aTime,
uint64_t* aOutInputBlockId = nullptr) {
PanGestureInput input(aType, MillisecondsSinceStartup(aTime), aTime, aPoint,
aDelta, 0 /* Modifiers */);
if (aType == PanGestureInput::PANGESTURE_END) {
input.mFollowedByMomentum = true;
}
return aTarget->ReceiveInputEvent(input, nullptr, aOutInputBlockId);
}
#endif // mozilla_layers_InputUtils_h

Просмотреть файл

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCTreeManagerTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
#include "mozilla/StaticPrefs.h"
class APZCSnappingOnMomentumTester : public APZCTreeManagerTester {};
TEST_F(APZCSnappingOnMomentumTester, Snap_On_Momentum) {
SCOPED_GFX_VAR(UseWebRender, bool, false);
const char* layerTreeSyntax = "c";
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0, 0, 100, 100)),
};
root =
CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
CSSRect(0, 0, 100, 500));
// Set up some basic scroll snapping
ScrollSnapInfo snap;
snap.mScrollSnapStrictnessY = StyleScrollSnapStrictness::Mandatory;
if (StaticPrefs::layout_css_scroll_snap_v1_enabled()) {
snap.mSnapPositionY.AppendElement(0 * AppUnitsPerCSSPixel());
snap.mSnapPositionY.AppendElement(100 * AppUnitsPerCSSPixel());
} else {
snap.mScrollSnapIntervalY = Some(100 * AppUnitsPerCSSPixel());
}
ScrollMetadata metadata = root->GetScrollMetadata(0);
metadata.SetSnapInfo(ScrollSnapInfo(snap));
root->SetScrollMetadata(metadata);
UniquePtr<ScopedLayerTreeRegistration> registration =
MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc);
manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
TimeStamp now = mcc->Time();
PanGesture(PanGestureInput::PANGESTURE_START, manager, ScreenIntPoint(50, 80),
ScreenPoint(0, 2), now);
mcc->AdvanceByMillis(5);
apzc->AdvanceAnimations(mcc->Time());
PanGesture(PanGestureInput::PANGESTURE_PAN, manager, ScreenIntPoint(50, 80),
ScreenPoint(0, 50), mcc->Time());
mcc->AdvanceByMillis(5);
apzc->AdvanceAnimations(mcc->Time());
PanGesture(PanGestureInput::PANGESTURE_END, manager, ScreenIntPoint(50, 80),
ScreenPoint(0, 0), mcc->Time());
mcc->AdvanceByMillis(5);
apzc->AdvanceAnimations(mcc->Time());
PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, manager,
ScreenIntPoint(50, 80), ScreenPoint(0, 200), mcc->Time());
mcc->AdvanceByMillis(10);
apzc->AdvanceAnimations(mcc->Time());
PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, manager,
ScreenIntPoint(50, 80), ScreenPoint(0, 50), mcc->Time());
mcc->AdvanceByMillis(10);
apzc->AdvanceAnimations(mcc->Time());
PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, manager,
ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
apzc->AdvanceAnimationsUntilEnd();
EXPECT_EQ(
100.0f,
apzc->GetCurrentAsyncScrollOffset(
AsyncPanZoomController::AsyncTransformConsumer::eForHitTesting)
.y);
}

Просмотреть файл

@ -19,6 +19,7 @@ if CONFIG['OS_TARGET'] != 'Android':
'TestGestureDetector.cpp',
'TestPinching.cpp',
'TestScrollHandoff.cpp',
'TestSnappingOnMomentum.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<html reftest-resolution="1.5">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.5">
<meta name="viewport" content="width=device-width">
<style>
body {
margin: 0;

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html class="reftest-wait">
<html class="reftest-wait" reftest-resolution="1.5">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.5">
<meta name="viewport" content="width=device-width">
<style>
body {
margin: 0;

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<html reftest-resolution="1.5">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.5">
<meta name="viewport" content="width=device-width">
<style>
body {
margin: 0;

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html class="reftest-wait">
<html class="reftest-wait" reftest-resolution="1.5">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.5">
<meta name="viewport" content="width=device-width">
<style>
body {
margin: 0;

Просмотреть файл

@ -15,8 +15,12 @@ fuzzy-if(Android,0-45,0-27) skip-if(!Android) pref(apz.allow_zooming,true) == as
# Test scrollbars working properly with pinch-zooming, i.e. different document resolutions.
# As above, the end of the scrollthumb won't match perfectly, but the bulk of the scrollbar should be present and identical.
fuzzy-if(Android,0-54,0-14) skip-if(!Android) pref(apz.allow_zooming,true) == scrollbar-zoom-resolution-1.html scrollbar-zoom-resolution-1-ref.html
fuzzy-if(Android,0-51,0-22) skip-if(!Android) pref(apz.allow_zooming,true) == scrollbar-zoom-resolution-2.html scrollbar-zoom-resolution-2-ref.html
# On desktop, even more fuzz is needed because thumb scaling is not exactly proportional: making the page twice as long
# won't make the thumb exactly twice as short, which is what the test expects. That's fine, as the purpose of the test is
# to catch more fundamental scrollbar rendering bugs such as the entire track being mispositioned or the thumb being
# clipped away.
fuzzy-if(Android,0-54,0-14) fuzzy-if(!Android,0-128,0-137) pref(apz.allow_zooming,true) == scrollbar-zoom-resolution-1.html scrollbar-zoom-resolution-1-ref.html
fuzzy-if(Android,0-51,0-22) fails-if(!Android) pref(apz.allow_zooming,true) == scrollbar-zoom-resolution-2.html scrollbar-zoom-resolution-2-ref.html
# Meta-viewport tag support
skip-if(!Android) pref(apz.allow_zooming,true) == initial-scale-1.html initial-scale-1-ref.html
@ -25,9 +29,5 @@ skip-if(!asyncPan) == frame-reconstruction-scroll-clamping.html frame-reconstruc
# Test that position:fixed and position:sticky elements are attached to the
# layout viewport.
#
# We skip these tests on Desktop platforms since they require container
# scrolling, which is enabled by default on Android, but behind a "Once" pref
# and cannot be enabled for individual reftests.
skip-if(!Android) pref(apz.allow_zooming,true) == pinch-zoom-position-fixed.html pinch-zoom-position-fixed-ref.html
skip-if(!Android) pref(apz.allow_zooming,true) == pinch-zoom-position-sticky.html pinch-zoom-position-sticky-ref.html
pref(apz.allow_zooming,true) == pinch-zoom-position-fixed.html pinch-zoom-position-fixed-ref.html
pref(apz.allow_zooming,true) == pinch-zoom-position-sticky.html pinch-zoom-position-sticky-ref.html

Просмотреть файл

@ -1,6 +1,6 @@
<!DOCTYPE html>
<html class="reftest-wait"><head>
<meta name="viewport" content="initial-scale=2.0; width=device-width">
<html class="reftest-wait" reftest-resolution="2.0"><head>
<meta name="viewport" content="width=device-width">
</head>
<body onload="scrollTo(225,5000); document.documentElement.classList.remove('reftest-wait')">
<div style="width: 4500px; height: 10000px; background: white;"></div>

Просмотреть файл

@ -1,6 +1,6 @@
<!DOCTYPE html>
<html class="reftest-wait"><head>
<meta name="viewport" content="initial-scale=0.5; width=device-width">
<html class="reftest-wait" reftest-resolution="0.5"><head>
<meta name="viewport" content="width=device-width">
</head>
<body onload="scrollTo(900,20000); document.documentElement.classList.remove('reftest-wait')">
<div style="width: 18000px; height: 40000px; background: white;"></div>

Просмотреть файл

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GPUVideoTextureClient.h"
#include "mozilla/VideoDecoderManagerChild.h"
#include "mozilla/RemoteDecoderManagerChild.h"
#include "mozilla/gfx/2D.h"
namespace mozilla {
@ -13,7 +13,7 @@ namespace layers {
using namespace gfx;
GPUVideoTextureData::GPUVideoTextureData(VideoDecoderManagerChild* aManager,
GPUVideoTextureData::GPUVideoTextureData(RemoteDecoderManagerChild* aManager,
const SurfaceDescriptorGPUVideo& aSD,
const gfx::IntSize& aSize)
: mManager(aManager), mSD(aSD), mSize(aSize) {}

Просмотреть файл

@ -13,13 +13,13 @@ namespace mozilla {
namespace gfx {
class SourceSurface;
}
class VideoDecoderManagerChild;
class RemoteDecoderManagerChild;
namespace layers {
class GPUVideoTextureData : public TextureData {
public:
GPUVideoTextureData(VideoDecoderManagerChild* aManager,
GPUVideoTextureData(RemoteDecoderManagerChild* aManager,
const SurfaceDescriptorGPUVideo& aSD,
const gfx::IntSize& aSize);
virtual ~GPUVideoTextureData();
@ -41,7 +41,7 @@ class GPUVideoTextureData : public TextureData {
GPUVideoTextureData* AsGPUVideoTextureData() override { return this; }
protected:
RefPtr<VideoDecoderManagerChild> mManager;
RefPtr<RemoteDecoderManagerChild> mManager;
SurfaceDescriptorGPUVideo mSD;
gfx::IntSize mSize;

Просмотреть файл

@ -97,7 +97,7 @@ class ContainerLayerComposite : public ContainerLayer, public LayerComposite {
// container layers don't use a compositable
CompositableHost* GetCompositableHost() override { return nullptr; }
// If the layer is marked as scale-to-resolution, add a post-scale
// If the layer has a pres shell resolution, add a post-scale
// to the layer's transform equal to the pres shell resolution we're
// scaling to. This cancels out the post scale of '1 / resolution'
// added by Layout. TODO: It would be nice to get rid of both of these

Просмотреть файл

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GPUVideoTextureHost.h"
#include "mozilla/VideoDecoderManagerParent.h"
#include "mozilla/RemoteDecoderManagerParent.h"
#include "ImageContainer.h"
#include "mozilla/layers/VideoBridgeParent.h"

Просмотреть файл

@ -913,10 +913,8 @@ void LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion,
// Set LayerScope begin/end frame
LayerScopeAutoFrame frame(PR_Now());
// Dump to console
if (StaticPrefs::LayersDump()) {
this->Dump(/* aSorted= */ true);
}
// If you're looking for the code to dump the layer tree, it was moved
// to CompositorBridgeParent::CompositeToTarget().
// Dump to LayerScope Viewer
if (LayerScope::CheckSendable()) {

Просмотреть файл

@ -1002,12 +1002,10 @@ void CompositorBridgeParent::CompositeToTarget(VsyncId aId, DrawTarget* aTarget,
RenderTraceLayers(mLayerManager->GetRoot(), "0000");
#ifdef MOZ_DUMP_PAINTING
if (StaticPrefs::DumpHostLayers()) {
if (StaticPrefs::DumpHostLayers() || StaticPrefs::LayersDump()) {
printf_stderr("Painting --- compositing layer tree:\n");
mLayerManager->Dump(/* aSorted = */ true);
}
#endif
mLayerManager->SetDebugOverlayWantsNextFrame(false);
mLayerManager->EndTransaction(time);

Просмотреть файл

@ -55,6 +55,15 @@ class ContainerLayerMLGPU final : public ContainerLayer, public LayerMLGPU {
void ComputeIntermediateSurfaceBounds();
// Similar to ContainerLayerComposite, we need to include the pres shell
// resolution, if there is one, in the layer's post-scale.
float GetPostXScale() const override {
return mSimpleAttrs.GetPostXScale() * mPresShellResolution;
}
float GetPostYScale() const override {
return mSimpleAttrs.GetPostYScale() * mPresShellResolution;
}
protected:
bool OnPrepareToRender(FrameBuilder* aBuilder) override;
void OnLayerManagerChange(LayerManagerMLGPU* aManager) override;

Просмотреть файл

@ -912,9 +912,7 @@ description =
description = See Bug 1505976 - investigate changing to async instead of matching GPU pattern
[PContent::LaunchRDDProcess]
description = See Bug 1518344 - investigate using async for PContent::LaunchRDDProcess
[PVideoDecoderManager::PVideoDecoder]
description =
[PVideoDecoderManager::Readback]
[PRemoteDecoderManager::Readback]
description =
[PBackgroundStorage::Preload]
description =

Просмотреть файл

@ -1256,10 +1256,9 @@ JS_PUBLIC_API void AddPersistentRoot(JSRuntime* rt, RootKind kind,
*
* These roots can be used in heap-allocated data structures, so they are not
* associated with any particular JSContext or stack. They are registered with
* the JSRuntime itself, without locking, so they require a full JSContext to be
* initialized, not one of its more restricted superclasses. Initialization may
* take place on construction, or in two phases if the no-argument constructor
* is called followed by init().
* the JSRuntime itself, without locking. Initialization may take place on
* construction, or in two phases if the no-argument constructor is called
* followed by init().
*
* Note that you must not use an PersistentRooted in an object owned by a JS
* object:
@ -1355,8 +1354,14 @@ class PersistentRooted
bool initialized() { return ListBase::isInList(); }
void init(JSContext* cx) { init(cx, SafelyInitialized<T>()); }
void init(RootingContext* cx) { init(cx, SafelyInitialized<T>()); }
void init(JSContext* cx) { init(RootingContext::get(cx)); }
template <typename U>
void init(RootingContext* cx, U&& initial) {
ptr = std::forward<U>(initial);
registerWithRootLists(cx);
}
template <typename U>
void init(JSContext* cx, U&& initial) {
ptr = std::forward<U>(initial);

Просмотреть файл

@ -66,6 +66,12 @@ class JS_PUBLIC_API JSTracer {
// everything reachable by regular edges has been marked.
Marking,
// Same as Marking, except we have now moved on to the "weak marking
// phase", in which every marked obj/script is immediately looked up to
// see if it is a weak map key (and therefore might require marking its
// weak map value).
WeakMarking,
// A tracer that traverses the graph for the purposes of moving objects
// from the nursery to the tenured area.
Tenuring,
@ -74,7 +80,12 @@ class JS_PUBLIC_API JSTracer {
// Traversing children is the responsibility of the callback.
Callback
};
bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking; }
bool isMarkingTracer() const {
return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking;
}
bool isWeakMarkingTracer() const {
return tag_ == TracerKindTag::WeakMarking;
}
bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
inline JS::CallbackTracer* asCallbackTracer();

Просмотреть файл

@ -92,10 +92,7 @@ bool WeakMapObject::get(JSContext* cx, unsigned argc, Value* vp) {
if (ObjectValueMap* map =
args.thisv().toObject().as<WeakMapObject>().getMap()) {
JSObject* key = &args[0].toObject();
// The lookup here is only used for the removal, so we can skip the read
// barrier. This is not very important for performance, but makes it easier
// to test nonbarriered removal from internal weakmaps (eg Debugger maps.)
if (ObjectValueMap::Ptr ptr = map->unbarrieredLookup(key)) {
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
map->remove(ptr);
args.rval().setBoolean(true);
return true;

Просмотреть файл

@ -439,34 +439,6 @@ static MOZ_ALWAYS_INLINE void AssertValidToSkipBarrier(TenuredCell* thing) {
AssertValidToSkipBarrier(next);
}
// Like gc::MarkColor but allows the possibility of the cell being
// unmarked. Order is important here, with white being 'least marked'
// and black being 'most marked'.
enum class CellColor : uint8_t { White = 0, Gray = 1, Black = 2 };
inline CellColor GetCellColor(Cell* cell) {
if (cell->isMarkedBlack()) {
return CellColor::Black;
}
if (cell->isMarkedGray()) {
return CellColor::Gray;
}
return CellColor::White;
}
static inline CellColor GetCellColor(MarkColor color) {
return color == MarkColor::Black ? CellColor::Black : CellColor::Gray;
}
static inline MarkColor GetMarkColor(CellColor color) {
MOZ_ASSERT(color != CellColor::White);
return color == CellColor::Black ? MarkColor::Black : MarkColor::Gray;
}
static inline bool IsMarked(CellColor c) { return c != CellColor::White; }
#ifdef DEBUG
/* static */ void Cell::assertThingIsNotGray(Cell* cell) {

Просмотреть файл

@ -4596,46 +4596,39 @@ void GCRuntime::updateMallocCountersOnGCStart() {
}
template <class ZoneIterT>
IncrementalProgress GCRuntime::markWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget) {
void GCRuntime::markWeakReferences(gcstats::PhaseKind phase) {
MOZ_ASSERT(marker.isDrained());
gcstats::AutoPhase ap1(stats(), phase);
// We may have already entered weak marking mode, in which case this will do
// nothing.
marker.enterWeakMarkingMode();
// This is not strictly necessary; if we yield here, we could run the mutator
// in weak marking mode and unmark gray would end up doing the key lookups.
// But it seems better to not slow down barriers. Re-entering weak marking
// mode will be fast since already-processed markables have been removed.
auto leaveOnExit =
mozilla::MakeScopeExit([&] { marker.leaveWeakMarkingMode(); });
// TODO bug 1167452: Make weak marking incremental
drainMarkStack();
bool markedAny = true;
while (markedAny) {
if (!marker.markUntilBudgetExhausted(budget)) {
return NotFinished;
}
markedAny = false;
if (!marker.isWeakMarking()) {
for (;;) {
bool markedAny = false;
if (!marker.isWeakMarkingTracer()) {
for (ZoneIterT zone(rt); !zone.done(); zone.next()) {
markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
}
}
markedAny |= Debugger::markIteratively(&marker);
markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
if (!markedAny) {
break;
}
drainMarkStack();
}
MOZ_ASSERT(marker.isDrained());
return Finished;
marker.leaveWeakMarkingMode();
}
IncrementalProgress GCRuntime::markWeakReferencesInCurrentGroup(
gcstats::PhaseKind phase, SliceBudget& budget) {
return markWeakReferences<SweepGroupZonesIter>(phase, budget);
void GCRuntime::markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase) {
markWeakReferences<SweepGroupZonesIter>(phase);
}
template <class ZoneIterT>
@ -4655,9 +4648,8 @@ void GCRuntime::markGrayRoots(gcstats::PhaseKind phase) {
}
}
IncrementalProgress GCRuntime::markAllWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget) {
return markWeakReferences<GCZonesIter>(phase, budget);
void GCRuntime::markAllWeakReferences(gcstats::PhaseKind phase) {
markWeakReferences<GCZonesIter>(phase);
}
void GCRuntime::markAllGrayReferences(gcstats::PhaseKind phase) {
@ -4745,7 +4737,7 @@ void js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session) {
* collecting.
*/
WeakMapColors markedWeakMaps;
WeakMapSet markedWeakMaps;
/*
* For saving, smush all of the keys into one big table and split them back
@ -4821,8 +4813,7 @@ void js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session) {
gcstats::AutoPhase ap1(gc->stats(), gcstats::PhaseKind::SWEEP);
gcstats::AutoPhase ap2(gc->stats(), gcstats::PhaseKind::SWEEP_MARK);
auto unlimited = SliceBudget::unlimited();
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_WEAK, unlimited);
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_WEAK);
/* Update zone state for gray marking. */
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
@ -4832,8 +4823,7 @@ void js::gc::MarkingValidator::nonIncrementalMark(AutoGCSession& session) {
AutoSetMarkColor setColorGray(gc->marker, MarkColor::Gray);
gc->markAllGrayReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY);
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK,
unlimited);
gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK);
/* Restore zone state. */
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
@ -5504,18 +5494,12 @@ IncrementalProgress GCRuntime::endMarkingSweepGroup(FreeOp* fop,
gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_MARK);
if (markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_WEAK,
budget) == NotFinished) {
return NotFinished;
}
markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_WEAK);
AutoSetMarkColor setColorGray(marker, MarkColor::Gray);
// Mark transitively inside the current compartment group.
if (markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK,
budget) == NotFinished) {
return NotFinished;
}
markWeakReferencesInCurrentGroup(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK);
MOZ_ASSERT(marker.isDrained());

Просмотреть файл

@ -225,26 +225,6 @@ class MarkStackIter {
} /* namespace gc */
enum MarkingState : uint8_t {
// Have not yet started marking.
NotActive,
// Main marking mode. Weakmap marking will be populating the weakKeys tables
// but not consulting them. The state will transition to WeakMarking until it
// is done, then back to RegularMarking.
RegularMarking,
// Same as RegularMarking except now every marked obj/script is immediately
// looked up in the weakKeys table to see if it is a weakmap key, and
// therefore might require marking its value. Transitions back to
// RegularMarking when done.
WeakMarking,
// Same as RegularMarking, but we OOMed (or obeyed a directive in the test
// marking queue) and fell back to iterating until the next GC.
IterativeMarking
};
class GCMarker : public JSTracer {
public:
explicit GCMarker(JSRuntime* rt);
@ -311,24 +291,12 @@ class GCMarker : public JSTracer {
void enterWeakMarkingMode();
void leaveWeakMarkingMode();
void abortLinearWeakMarking() {
if (state == MarkingState::WeakMarking) {
leaveWeakMarkingMode();
}
state = MarkingState::IterativeMarking;
leaveWeakMarkingMode();
linearWeakMarkingDisabled_ = true;
}
void delayMarkingChildren(gc::Cell* cell);
// Remove <map,toRemove> from the weak keys table indexed by 'key'.
void forgetWeakKey(js::gc::WeakKeyTable& weakKeys, WeakMapBase* map,
gc::Cell* keyOrDelegate, gc::Cell* keyToRemove);
// Purge all mention of 'map' from the weak keys table.
void forgetWeakMap(WeakMapBase* map, Zone* zone);
// 'delegate' is no longer the delegate of 'key'.
void severWeakDelegate(JSObject* key, JSObject* delegate);
bool isDrained() { return isMarkStackEmpty() && !delayedMarkingList; }
// The mark queue is a testing-only feature for controlling mark ordering and
@ -366,8 +334,6 @@ class GCMarker : public JSTracer {
template <typename T>
void markImplicitEdges(T* oldThing);
bool isWeakMarking() const { return state == MarkingState::WeakMarking; }
private:
#ifdef DEBUG
void checkZone(void* p);
@ -457,16 +423,22 @@ class GCMarker : public JSTracer {
/* Whether more work has been added to the delayed marking list. */
MainThreadData<bool> delayedMarkingWorkAdded;
/*
* If the weakKeys table OOMs, disable the linear algorithm and fall back
* to iterating until the next GC.
*/
MainThreadData<bool> linearWeakMarkingDisabled_;
/* The count of marked objects during GC. */
size_t markCount;
/* Track the state of marking. */
MainThreadData<MarkingState> state;
#ifdef DEBUG
/* Count of arenas that are currently in the stack. */
MainThreadData<size_t> markLaterArenas;
/* Assert that start and stop are called with correct ordering. */
MainThreadData<bool> started;
/* The test marking queue might want to be marking a particular color. */
mozilla::Maybe<js::gc::MarkColor> queueMarkColor;
@ -513,11 +485,6 @@ class MOZ_RAII AutoSetMarkColor {
marker_.setMarkColor(newColor);
}
AutoSetMarkColor(GCMarker& marker, CellColor newColor)
: AutoSetMarkColor(marker, GetMarkColor(newColor)) {
MOZ_ASSERT(newColor != CellColor::White);
}
~AutoSetMarkColor() { marker_.setMarkColor(initialColor_); }
};

Просмотреть файл

@ -624,15 +624,12 @@ class GCRuntime {
gcstats::PhaseKind phase);
void drainMarkStack();
template <class ZoneIterT>
IncrementalProgress markWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget);
IncrementalProgress markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase,
SliceBudget& budget);
void markWeakReferences(gcstats::PhaseKind phase);
void markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase);
template <class ZoneIterT>
void markGrayRoots(gcstats::PhaseKind phase);
void markBufferedGrayRoots(JS::Zone* zone);
IncrementalProgress markAllWeakReferences(gcstats::PhaseKind phase,
SliceBudget& budget);
void markAllWeakReferences(gcstats::PhaseKind phase);
void markAllGrayReferences(gcstats::PhaseKind phase);
void beginSweepPhase(JS::GCReason reason, AutoGCSession& session);

Просмотреть файл

@ -110,12 +110,8 @@ PhaseKindGraphRoots = [
UnmarkGrayPhaseKind,
]),
PhaseKind("SWEEP_MARK_INCOMING_GRAY", "Mark Incoming Gray Pointers", 14),
PhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15, [
UnmarkGrayPhaseKind,
]),
PhaseKind("SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16, [
UnmarkGrayPhaseKind,
]),
PhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15),
PhaseKind("SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16)
]),
PhaseKind("FINALIZE_START", "Finalize Start Callbacks", 17, [
PhaseKind("WEAK_ZONES_CALLBACK", "Per-Slice Weak Callback", 57),

Просмотреть файл

@ -35,7 +35,6 @@
#include "gc/GC-inl.h"
#include "gc/Nursery-inl.h"
#include "gc/PrivateIterators-inl.h"
#include "gc/WeakMap-inl.h"
#include "gc/Zone-inl.h"
#include "vm/GeckoProfiler-inl.h"
#include "vm/NativeObject-inl.h"
@ -617,6 +616,11 @@ void GCMarker::markEphemeronValues(gc::Cell* markedCell,
DebugOnly<size_t> initialLen = values.length();
for (const auto& markable : values) {
if (color == gc::MarkColor::Black &&
markable.weakmap->markColor == gc::MarkColor::Gray) {
continue;
}
markable.weakmap->markEntry(this, markedCell, markable.key);
}
@ -626,76 +630,9 @@ void GCMarker::markEphemeronValues(gc::Cell* markedCell,
MOZ_ASSERT(values.length() == initialLen);
}
void GCMarker::forgetWeakKey(js::gc::WeakKeyTable& weakKeys, WeakMapBase* map,
gc::Cell* keyOrDelegate, gc::Cell* keyToRemove) {
// Find and remove the exact pair <map,keyToRemove> from the values of the
// weak keys table.
//
// This function is called when 'keyToRemove' is removed from a weakmap
// 'map'. If 'keyToRemove' has a delegate, then the delegate will be used as
// the lookup key in gcWeakKeys; otherwise, 'keyToRemove' itself will be. In
// either case, 'keyToRemove' is what we will be filtering out of the
// Markable values in the weakKey table.
auto p = weakKeys.get(keyOrDelegate);
// Note that this is not guaranteed to find anything. The key will have
// only been inserted into the weakKeys table if it was unmarked when the
// map was traced.
if (p) {
EraseIf(p->value, [map, keyToRemove](const WeakMarkable& markable) -> bool {
// Note that we should only have had the key in weakKeys if the map
// was marked.
MOZ_ASSERT(IsMarked(markable.weakmap->markColor));
return (markable.weakmap == map) && (markable.key == keyToRemove);
});
}
} // namespace js
void GCMarker::forgetWeakMap(WeakMapBase* map, Zone* zone) {
for (auto p = zone->gcNurseryWeakKeys().all(); !p.empty(); p.popFront()) {
EraseIf(p.front().value, [map](const WeakMarkable& markable) -> bool {
return markable.weakmap == map;
});
}
for (auto p = zone->gcWeakKeys().all(); !p.empty(); p.popFront()) {
EraseIf(p.front().value, [map](const WeakMarkable& markable) -> bool {
return markable.weakmap == map;
});
}
}
// 'delegate' is no longer the delegate of 'key'.
void GCMarker::severWeakDelegate(JSObject* key, JSObject* delegate) {
JS::Zone* zone = delegate->zone();
bool found = true;
while (found) {
found = false;
auto p = zone->gcWeakKeys(delegate).get(delegate);
if (!p) {
// The key may not even be in the weak key table, eg if it was already
// marked when marking the map.
break;
}
for (auto i = p->value.begin(); i != p->value.end(); i++) {
if (i->key == key) {
WeakMapBase* weakmap = i->weakmap;
p->value.erase(i);
// Warning: the key and delegate can be same-zone (even though they're
// different-compartment). This line will insert into the weak keys
// table and so can invalidate the pointer |p| on a rehash. So redo the
// lookup after each one of these.
weakmap->postSeverDelegate(this, key);
found = true;
break;
}
}
}
}
template <typename T>
void GCMarker::markImplicitEdgesHelper(T markedThing) {
if (state != MarkingState::WeakMarking) {
if (!isWeakMarkingTracer()) {
return;
}
@ -1597,7 +1534,7 @@ GCMarker::MarkQueueProgress GCMarker::processMarkQueue() {
return QueueYielded;
} else if (js::StringEqualsAscii(str, "enter-weak-marking-mode") ||
js::StringEqualsAscii(str, "abort-weak-marking-mode")) {
if (state == MarkingState::RegularMarking) {
if (!isWeakMarkingTracer() && !linearWeakMarkingDisabled_) {
// We can't enter weak marking mode at just any time, so instead
// we'll stop processing the queue and continue on with the GC. Once
// we enter weak marking mode, we can continue to the rest of the
@ -2429,11 +2366,11 @@ GCMarker::GCMarker(JSRuntime* rt)
grayStack(),
color(MarkColor::Black),
delayedMarkingList(nullptr),
delayedMarkingWorkAdded(false),
state(MarkingState::NotActive)
delayedMarkingWorkAdded(false)
#ifdef DEBUG
,
markLaterArenas(0),
started(false),
strictCompartmentChecking(false),
markQueue(rt),
queuePos(0)
@ -2446,9 +2383,12 @@ bool GCMarker::init(JSGCMode gcMode) {
}
void GCMarker::start() {
MOZ_ASSERT(state == MarkingState::NotActive);
state = MarkingState::RegularMarking;
#ifdef DEBUG
MOZ_ASSERT(!started);
started = true;
#endif
color = MarkColor::Black;
linearWeakMarkingDisabled_ = false;
#ifdef DEBUG
queuePos = 0;
@ -2460,11 +2400,15 @@ void GCMarker::start() {
}
void GCMarker::stop() {
#ifdef DEBUG
MOZ_ASSERT(isDrained());
MOZ_ASSERT(started);
started = false;
MOZ_ASSERT(!delayedMarkingList);
MOZ_ASSERT(markLaterArenas == 0);
MOZ_ASSERT(state != MarkingState::NotActive);
state = MarkingState::NotActive;
#endif
/* Free non-ballast stack memory. */
blackStack.clear();
@ -2565,89 +2509,50 @@ void GCMarker::repush(JSObject* obj) {
void GCMarker::enterWeakMarkingMode() {
MOZ_ASSERT(runtime()->gc.nursery().isEmpty());
MOZ_ASSERT(isMarkingTracer());
if (state != MarkingState::RegularMarking) {
MOZ_ASSERT(tag_ == TracerKindTag::Marking);
if (linearWeakMarkingDisabled_) {
return;
}
if (weakMapAction() != ExpandWeakMaps) {
return; // This marker does not do linear-time weak marking.
}
// During weak marking mode, we maintain a table mapping weak keys to
// entries in known-live weakmaps. Initialize it with the keys of marked
// weakmaps -- or more precisely, the keys of marked weakmaps that are
// mapped to not yet live values. (Once bug 1167452 implements incremental
// weakmap marking, this initialization step will become unnecessary, as
// the table will already hold all such keys.)
if (weakMapAction() == ExpandWeakMaps) {
tag_ = TracerKindTag::WeakMarking;
// Set state before doing anything else, so any new key that is marked
// during the following gcWeakKeys scan will itself be looked up in
// gcWeakKeys and marked according to ephemeron rules.
state = MarkingState::WeakMarking;
// If there was an 'enter-weak-marking-mode' token in the queue, then it
// and everything after it will still be in the queue so we can process
// them now.
while (processMarkQueue() == QueueYielded) {
};
// If there was an 'enter-weak-marking-mode' token in the queue, then it
// and everything after it will still be in the queue so we can process
// them now.
while (processMarkQueue() == QueueYielded) {
};
// gcWeakKeys contains the keys from all weakmaps marked so far, or at least
// the keys that might still need to be marked through. Scan through
// gcWeakKeys and mark all values whose keys are marked. This marking may
// recursively mark through other weakmap entries (by looking them up in
// gcWeakKeys, since we are now in WeakMarking mode). Once the mark stack is
// drained, the end result is a consistent state where all values are marked
// if both their map and key are marked -- though note that we may later leave
// weak marking mode, do some more marking, and then enter back in.
for (SweepGroupZonesIter zone(runtime(), js::SkipAtoms); !zone.done();
zone.next()) {
if (!zone->isGCMarking()) {
continue;
}
MOZ_ASSERT(zone->gcNurseryWeakKeys().count() == 0);
// An OrderedHashMap::Range stays valid even when the underlying table
// (zone->gcWeakKeys) is mutated, which is useful here since we may add
// additional entries while iterating over the Range.
gc::WeakKeyTable::Range r = zone->gcWeakKeys().all();
while (!r.empty()) {
gc::Cell* key = r.front().key;
gc::CellColor keyColor = GetCellColor(key);
if (IsMarked(keyColor)) {
MOZ_ASSERT(key == r.front().key);
auto& markables = r.front().value;
r.popFront(); // Pop before any mutations happen.
size_t end = markables.length();
for (size_t i = 0; i < end; i++) {
WeakMarkable& v = markables[i];
// Note: if the key is marked gray but not black, then the markables
// vector may be appended to within this loop body. So iterate just
// over the ones from before weak marking mode was switched on.
v.weakmap->markEntry(this, key, v.key);
for (SweepGroupZonesIter zone(runtime()); !zone.done(); zone.next()) {
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (m->marked) {
(void)m->markEntries(this);
}
if (keyColor == gc::CellColor::Black) {
// We can't mark the key any more than already is, so it no longer
// needs to be in the weak keys table.
if (end == markables.length()) {
bool found;
zone->gcWeakKeys().remove(key, &found);
} else {
markables.erase(markables.begin(), &markables[end]);
}
}
} else {
r.popFront();
}
}
}
}
void GCMarker::leaveWeakMarkingMode() {
MOZ_ASSERT_IF(weakMapAction() == ExpandWeakMaps,
state == MarkingState::WeakMarking ||
state == MarkingState::IterativeMarking);
if (state != MarkingState::IterativeMarking) {
state = MarkingState::RegularMarking;
}
MOZ_ASSERT_IF(
weakMapAction() == ExpandWeakMaps && !linearWeakMarkingDisabled_,
tag_ == TracerKindTag::WeakMarking);
tag_ = TracerKindTag::Marking;
// The gcWeakKeys table is still populated and may be used during a future
// weak marking mode within this GC.
// Table is expensive to maintain when not in weak marking mode, so we'll
// rebuild it upon entry rather than allow it to contain stale data.
AutoEnterOOMUnsafeRegion oomUnsafe;
for (GCZonesIter zone(runtime()); !zone.done(); zone.next()) {
if (!zone->gcWeakKeys().clear()) {
oomUnsafe.crash("clearing weak keys in GCMarker::leaveWeakMarkingMode()");
}
}
}
void GCMarker::delayMarkingChildren(Cell* cell) {
@ -2796,7 +2701,7 @@ void gc::PushArena(GCMarker* gcmarker, Arena* arena) {
#ifdef DEBUG
void GCMarker::checkZone(void* p) {
MOZ_ASSERT(state != MarkingState::NotActive);
MOZ_ASSERT(started);
DebugOnly<Cell*> cell = static_cast<Cell*>(p);
MOZ_ASSERT_IF(cell->isTenured(), cell->asTenured().zone()->isCollecting());
}

Просмотреть файл

@ -763,7 +763,8 @@ bool js::gc::CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key,
Zone* valueZone = GetCellZoneFromAnyThread(value);
MOZ_ASSERT(valueZone == zone || valueZone->isAtomsZone());
CellColor mapColor = map->markColor;
CellColor mapColor =
map->markColor == MarkColor::Black ? CellColor::Black : CellColor::Gray;
if (object && GetCellColor(object) != mapColor) {
fprintf(stderr, "WeakMap object is marked differently to the map\n");
fprintf(stderr, "(map %p is %s, object %p is %s)\n", map,

Просмотреть файл

@ -16,12 +16,32 @@
namespace js {
namespace gc {
// Like gc::MarkColor but allows the possibility of the cell being
// unmarked.
enum class CellColor : uint8_t {
White = 0,
Gray = uint8_t(MarkColor::Gray),
Black = uint8_t(MarkColor::Black)
};
static constexpr CellColor AllCellColors[] = {CellColor::White, CellColor::Gray,
CellColor::Black};
static constexpr CellColor MarkedCellColors[] = {CellColor::Gray,
CellColor::Black};
inline CellColor GetCellColor(Cell* cell) {
if (cell->isMarkedBlack()) {
return CellColor::Black;
}
if (cell->isMarkedGray()) {
return CellColor::Gray;
}
return CellColor::White;
}
inline CellColor ExpectedWeakMapValueColor(CellColor keyColor,
CellColor mapColor) {
return Min(keyColor, mapColor);

Просмотреть файл

@ -8,15 +8,12 @@
#define gc_WeakMap_inl_h
#include "gc/WeakMap.h"
#include "gc/PublicIterators.h"
#include "gc/Zone.h"
#include "js/TraceKind.h"
#include "vm/JSContext.h"
namespace js {
namespace gc {
namespace detail {
template <typename T>
static T extractUnbarriered(const WriteBarriered<T>& v) {
@ -28,45 +25,18 @@ static T* extractUnbarriered(T* v) {
return v;
}
template <typename T>
static JS::Zone* GetZone(T t) {
return t->zone();
inline /* static */ JSObject* WeakMapBase::getDelegate(JSObject* key) {
return UncheckedUnwrapWithoutExpose(key);
}
static JS::Zone* GetZone(js::HeapPtr<JS::Value>& t) {
if (!t.isGCThing()) {
return nullptr;
}
return t.toGCThing()->asTenured().zone();
}
// Only objects have delegates, so default to returning nullptr. Note that some
// compilation units will only ever use the object version.
static MOZ_MAYBE_UNUSED JSObject* GetDelegateHelper(gc::Cell* key) {
inline /* static */ JSObject* WeakMapBase::getDelegate(JSScript* script) {
return nullptr;
}
static JSObject* GetDelegateHelper(JSObject* key) {
JSObject* delegate = UncheckedUnwrapWithoutExpose(key);
return (key == delegate) ? nullptr : delegate;
inline /* static */ JSObject* WeakMapBase::getDelegate(LazyScript* script) {
return nullptr;
}
} /* namespace detail */
} /* namespace gc */
// Use a helper function to do overload resolution to handle cases like
// Heap<ObjectSubclass*>: find everything that is convertible to JSObject* (and
// avoid calling barriers).
template <typename T>
inline /* static */ JSObject* WeakMapBase::getDelegate(const T& key) {
using namespace gc::detail;
return GetDelegateHelper(extractUnbarriered(key));
}
template <>
inline /* static */ JSObject* WeakMapBase::getDelegate(gc::Cell* const&) =
delete;
template <class K, class V>
WeakMap<K, V>::WeakMap(JSContext* cx, JSObject* memOf)
: Base(cx->zone()), WeakMapBase(memOf, cx->zone()) {
@ -82,22 +52,11 @@ WeakMap<K, V>::WeakMap(JSContext* cx, JSObject* memOf)
zone()->gcWeakMapList().insertFront(this);
if (zone()->wasGCStarted()) {
markColor = CellColor::Black;
marked = true;
markColor = gc::MarkColor::Black;
}
}
namespace gc {
// Compute the correct color to mark a weakmap entry value based on the map and
// key colors.
struct AutoSetValueColor : gc::AutoSetMarkColor {
AutoSetValueColor(GCMarker& marker, CellColor mapColor, CellColor keyColor)
: gc::AutoSetMarkColor(
marker, mapColor == keyColor ? mapColor : CellColor::Gray) {}
};
} // namespace gc
// Trace a WeakMap entry based on 'markedCell' getting marked, where 'origKey'
// is the key in the weakmap. These will probably be the same, but can be
// different eg when markedCell is a delegate for origKey.
@ -105,40 +64,25 @@ struct AutoSetValueColor : gc::AutoSetMarkColor {
// This implementation does not use 'markedCell'; it looks up origKey and checks
// the mark bits on everything it cares about, one of which will be
// markedCell. But a subclass might use it to optimize the liveness check.
//
// markEntry is called when encountering a weakmap key during marking, or when
// entering weak marking mode.
template <class K, class V>
void WeakMap<K, V>::markEntry(GCMarker* marker, gc::Cell* markedCell,
gc::Cell* origKey) {
using namespace gc::detail;
MOZ_ASSERT(marked);
Ptr p = Base::lookup(static_cast<Lookup>(origKey));
// We should only be processing <weakmap,key> pairs where the key exists in
// the weakmap. Such pairs are inserted when a weakmap is marked, and are
// removed by barriers if the key is removed from the weakmap. Failure here
// probably means gcWeakKeys is not being properly traced during a minor GC,
// or the weakmap keys are not being updated when tenured.
MOZ_ASSERT(p.found());
K key(p->key());
MOZ_ASSERT((markedCell == extractUnbarriered(key)) ||
(markedCell == getDelegate(key)));
CellColor delegateColor = getDelegateColor(key);
if (IsMarked(delegateColor) && key->zone()->isGCMarking()) {
gc::AutoSetMarkColor autoColor(*marker, delegateColor);
if (marker->isMarked(&key)) {
TraceEdge(marker, &p->value(), "ephemeron value");
} else if (keyNeedsMark(marker, key)) {
TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
TraceEdge(marker, &key, "proxy-preserved WeakMap ephemeron key");
MOZ_ASSERT(key == p->key(), "no moving GC");
}
CellColor keyColor = getCellColor(key);
if (IsMarked(keyColor)) {
JS::Zone* valueZone = GetZone(p->value());
if (valueZone && valueZone->isGCMarking()) {
gc::AutoSetValueColor autoColor(*marker, markColor, keyColor);
TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
}
MOZ_ASSERT(key == p->key()); // No moving
}
key.unsafeSet(nullptr); // Prevent destructor from running barriers.
}
template <class K, class V>
@ -154,12 +98,13 @@ void WeakMap<K, V>::trace(JSTracer* trc) {
// Don't change the map color from black to gray. This can happen when a
// barrier pushes the map object onto the black mark stack when it's already
// present on the gray mark stack, which is marked later.
if (markColor == CellColor::Black &&
if (marked && markColor == gc::MarkColor::Black &&
marker->markColor() == gc::MarkColor::Gray) {
return;
}
markColor = GetCellColor(marker->markColor());
marked = true;
markColor = marker->markColor();
(void)markEntries(marker);
return;
}
@ -186,7 +131,8 @@ template <class K, class V>
/* static */ void WeakMap<K, V>::addWeakEntry(
GCMarker* marker, gc::Cell* key, const gc::WeakMarkable& markable) {
Zone* zone = key->asTenured().zone();
auto& weakKeys = zone->gcWeakKeys(key);
auto& weakKeys =
gc::IsInsideNursery(key) ? zone->gcNurseryWeakKeys() : zone->gcWeakKeys();
auto p = weakKeys.get(key);
if (p) {
gc::WeakEntryVector& weakEntries = p->value;
@ -204,45 +150,39 @@ template <class K, class V>
template <class K, class V>
bool WeakMap<K, V>::markEntries(GCMarker* marker) {
MOZ_ASSERT(IsMarked(markColor));
MOZ_ASSERT(marked);
if (marker->markColor() == gc::MarkColor::Black &&
markColor == gc::MarkColor::Gray) {
return false;
}
bool markedAny = false;
for (Enum e(*this); !e.empty(); e.popFront()) {
// If the entry is live, ensure its key and value are marked.
CellColor keyColor = getCellColor(e.front().key().get());
CellColor delegateColor = getDelegateColor(e.front().key());
if (IsMarked(delegateColor) && keyColor < delegateColor) {
gc::AutoSetMarkColor autoColor(*marker, delegateColor);
bool keyIsMarked = marker->isMarked(&e.front().mutableKey());
if (!keyIsMarked && keyNeedsMark(marker, e.front().key())) {
TraceEdge(marker, &e.front().mutableKey(),
"proxy-preserved WeakMap entry key");
keyIsMarked = true;
markedAny = true;
keyColor = delegateColor;
}
if (IsMarked(keyColor)) {
gc::AutoSetValueColor autoColor(*marker, markColor, keyColor);
if (keyIsMarked) {
if (!marker->isMarked(&e.front().value())) {
TraceEdge(marker, &e.front().value(), "WeakMap entry value");
markedAny = true;
}
}
// Changes in the map's mark color will be handled in this code, but
// changes in the key's mark color are handled through the weak keys table.
// So we only need to populate the table if the key is less marked than the
// map, to catch later updates in the key's mark color.
if (keyColor < markColor) {
MOZ_ASSERT(marker->weakMapAction() == ExpandWeakMaps);
// Entry is not yet known to be live. Record this weakmap and the lookup
// key in the list of weak keys. If the key has a delegate, then the
// lookup key is the delegate (because marking the key will end up
// marking the delegate and thereby mark the entry.)
gc::Cell* weakKey = gc::detail::extractUnbarriered(e.front().key());
} else if (marker->isWeakMarkingTracer()) {
// Entry is not yet known to be live. Record this weakmap and
// the lookup key in the list of weak keys. Also record the
// delegate, if any, because marking the delegate also marks
// the entry.
gc::Cell* weakKey = extractUnbarriered(e.front().key());
gc::WeakMarkable markable(this, weakKey);
addWeakEntry(marker, weakKey, markable);
if (JSObject* delegate = getDelegate(e.front().key())) {
addWeakEntry(marker, delegate, markable);
} else {
addWeakEntry(marker, weakKey, markable);
}
}
}
@ -251,32 +191,25 @@ bool WeakMap<K, V>::markEntries(GCMarker* marker) {
}
template <class K, class V>
void WeakMap<K, V>::postSeverDelegate(GCMarker* marker, gc::Cell* key) {
if (IsMarked(markColor)) {
// We only stored the delegate, not the key, and we're severing the
// delegate from the key. So store the key.
gc::WeakMarkable markable(this, key);
addWeakEntry(marker, key, markable);
}
}
template <class K, class V>
inline WeakMapBase::CellColor WeakMap<K, V>::getDelegateColor(
JSObject* key) const {
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker, JSObject* key) const {
JSObject* delegate = getDelegate(key);
return delegate ? getCellColor(delegate) : CellColor::White;
/*
* Check if the delegate is marked with any color to properly handle
* gray marking when the key's delegate is black and the map is gray.
*/
return delegate && marker->isMarkedUnbarriered(&delegate);
}
template <class K, class V>
inline WeakMapBase::CellColor WeakMap<K, V>::getDelegateColor(
JSScript* script) const {
return CellColor::White;
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker,
JSScript* script) const {
return false;
}
template <class K, class V>
inline WeakMapBase::CellColor WeakMap<K, V>::getDelegateColor(
LazyScript* script) const {
return CellColor::White;
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker,
LazyScript* script) const {
return false;
}
template <class K, class V>

Просмотреть файл

@ -23,7 +23,7 @@ using namespace js;
using namespace js::gc;
WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
: memberOf(memOf), zone_(zone), markColor(CellColor::White) {
: memberOf(memOf), zone_(zone), marked(false), markColor(MarkColor::Black) {
MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
}
@ -32,14 +32,8 @@ WeakMapBase::~WeakMapBase() {
}
void WeakMapBase::unmarkZone(JS::Zone* zone) {
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!zone->gcWeakKeys().clear()) {
oomUnsafe.crash("clearing weak keys table");
}
MOZ_ASSERT(zone->gcNurseryWeakKeys().count() == 0);
for (WeakMapBase* m : zone->gcWeakMapList()) {
m->markColor = CellColor::White;
m->marked = false;
}
}
@ -58,7 +52,7 @@ bool WeakMapBase::checkMarkingForZone(JS::Zone* zone) {
bool ok = true;
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (IsMarked(m->markColor) && !m->checkMarking()) {
if (m->marked && !m->checkMarking()) {
ok = false;
}
}
@ -70,7 +64,7 @@ bool WeakMapBase::checkMarkingForZone(JS::Zone* zone) {
bool WeakMapBase::markZoneIteratively(JS::Zone* zone, GCMarker* marker) {
bool markedAny = false;
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (IsMarked(m->markColor) && m->markEntries(marker)) {
if (m->marked && m->markEntries(marker)) {
markedAny = true;
}
}
@ -89,7 +83,7 @@ bool WeakMapBase::findSweepGroupEdges(JS::Zone* zone) {
void WeakMapBase::sweepZone(JS::Zone* zone) {
for (WeakMapBase* m = zone->gcWeakMapList().getFirst(); m;) {
WeakMapBase* next = m->getNext();
if (IsMarked(m->markColor)) {
if (m->marked) {
m->sweep();
} else {
m->clearAndCompact();
@ -100,7 +94,7 @@ void WeakMapBase::sweepZone(JS::Zone* zone) {
#ifdef DEBUG
for (WeakMapBase* m : zone->gcWeakMapList()) {
MOZ_ASSERT(m->isInList() && IsMarked(m->markColor));
MOZ_ASSERT(m->isInList() && m->marked);
}
#endif
}
@ -117,24 +111,21 @@ void WeakMapBase::traceAllMappings(WeakMapTracer* tracer) {
}
bool WeakMapBase::saveZoneMarkedWeakMaps(JS::Zone* zone,
WeakMapColors& markedWeakMaps) {
WeakMapSet& markedWeakMaps) {
for (WeakMapBase* m : zone->gcWeakMapList()) {
if (IsMarked(m->markColor)) {
if (!markedWeakMaps.put(m, m->markColor)) {
return false;
}
if (m->marked && !markedWeakMaps.put(m)) {
return false;
}
}
return true;
}
void WeakMapBase::restoreMarkedWeakMaps(WeakMapColors& markedWeakMaps) {
for (WeakMapColors::Range r = markedWeakMaps.all(); !r.empty();
r.popFront()) {
WeakMapBase* map = r.front().key();
void WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps) {
for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
WeakMapBase* map = r.front();
MOZ_ASSERT(map->zone()->isGCMarking());
MOZ_ASSERT(map->markColor == CellColor::White);
map->markColor = r.front().value();
MOZ_ASSERT(!map->marked);
map->marked = true;
}
}
@ -143,15 +134,17 @@ size_t ObjectValueMap::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
}
bool ObjectValueMap::findZoneEdges() {
// For weakmap keys with delegates in a different zone, add a zone edge to
// ensure that the delegate zone finishes marking before the key zone.
//
// Possibly add an edge the other direction too, because scanning the
// gcWeakKeys for a zone containing a delegate might end up marking a value
// in the map/key zone.
/*
* For unmarked weakmap keys with delegates in a different zone, add a zone
* edge to ensure that the delegate zone finishes marking before the key
* zone.
*/
JS::AutoSuppressGCAnalysis nogc;
for (Range r = all(); !r.empty(); r.popFront()) {
JSObject* key = r.front().key();
if (key->asTenured().isMarkedBlack()) {
continue;
}
JSObject* delegate = getDelegate(key);
if (!delegate) {
continue;
@ -163,30 +156,6 @@ bool ObjectValueMap::findZoneEdges() {
if (!delegateZone->addSweepGroupEdgeTo(key->zone())) {
return false;
}
// The various cases depend on the order that the map and key are marked:
//
// If the key is marked:
// key marked, then map marked:
// - value was marked with map
// map marked, key already in map, key marked before weak marking mode:
// - key added to weakKeys when map marked
// - value marked during enterWeakMarkingMode: this requires the
// delegate's zone to be marked before the key zone, since only the
// delegate will be in weakKeys.
// map marked, key already in map, key marked after weak marking mode:
// - during key marking, weakKeys[key] triggers marking of value
// map marked, key inserted into map, key marked:
// - value marked by insert barrier
//
// In all cases, a marked key will have already marked the value, so there
// is no need for a key->delegate zone. We still need the delegate->key
// edge for the enterWeakMarkingMode case described above.
if (!key->asTenured().isMarkedBlack()) {
if (!key->zone()->addSweepGroupEdgeTo(delegateZone)) {
return false;
}
}
}
return true;
}

Просмотреть файл

@ -12,7 +12,6 @@
#include "gc/Barrier.h"
#include "gc/DeletePolicy.h"
#include "gc/Tracer.h"
#include "gc/Zone.h"
#include "gc/ZoneAllocator.h"
#include "js/HashTable.h"
@ -48,24 +47,13 @@ bool CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key, Cell* value);
// the implicit edges stored in the map) and of removing (sweeping) table
// entries when collection is complete.
using WeakMapColors = HashMap<WeakMapBase*, js::gc::CellColor,
DefaultHasher<WeakMapBase*>, SystemAllocPolicy>;
typedef HashSet<WeakMapBase*, DefaultHasher<WeakMapBase*>, SystemAllocPolicy>
WeakMapSet;
// Common base class for all WeakMap specializations, used for calling
// subclasses' GC-related methods.
class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
public:
friend class js::GCMarker;
using CellColor = js::gc::CellColor;
protected:
template <typename T>
CellColor getCellColor(const T& k) const {
if (!k->zone()->shouldMarkInZone() || !k->isTenured()) {
return CellColor::Black;
}
return GetCellColor(k);
}
public:
WeakMapBase(JSObject* memOf, JS::Zone* zone);
@ -95,25 +83,23 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
// entries of live weak maps whose keys are dead.
static void sweepZone(JS::Zone* zone);
// Sweep the marked weak maps in a zone, updating moved keys.
static void sweepZoneAfterMinorGC(JS::Zone* zone);
// Trace all weak map bindings. Used by the cycle collector.
static void traceAllMappings(WeakMapTracer* tracer);
// Save information about which weak maps are marked for a zone.
static bool saveZoneMarkedWeakMaps(JS::Zone* zone,
WeakMapColors& markedWeakMaps);
WeakMapSet& markedWeakMaps);
// Restore information about which weak maps are marked for many zones.
static void restoreMarkedWeakMaps(WeakMapColors& markedWeakMaps);
static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
#if defined(JS_GC_ZEAL) || defined(DEBUG)
static bool checkMarkingForZone(JS::Zone* zone);
#endif
template <typename T>
static JSObject* getDelegate(const T& key);
static JSObject* getDelegate(JSObject* key);
static JSObject* getDelegate(JSScript* script);
static JSObject* getDelegate(LazyScript* script);
protected:
// Instance member functions called by the above. Instantiations of WeakMap
@ -129,11 +115,6 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
virtual void markEntry(GCMarker* marker, gc::Cell* markedCell,
gc::Cell* l) = 0;
// An unmarked CCW with a delegate will add a weakKeys entry for the
// delegate. If the delegate is removed with NukeCrossCompartmentWrapper,
// then the (former) CCW needs to be added to weakKeys instead.
virtual void postSeverDelegate(GCMarker* marker, gc::Cell* key) = 0;
virtual bool markEntries(GCMarker* marker) = 0;
#ifdef JS_GC_ZEAL
@ -151,21 +132,10 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase> {
// Whether this object has been marked during garbage collection and which
// color it was marked.
gc::CellColor markColor;
bool marked;
gc::MarkColor markColor;
};
namespace detail {
template <typename T>
struct RemoveBarrier {};
template <typename T>
struct RemoveBarrier<js::HeapPtr<T>> {
using Type = T;
};
} // namespace detail
template <class Key, class Value>
class WeakMap
: private HashMap<Key, Value, MovableCellHasher<Key>, ZoneAllocPolicy>,
@ -191,8 +161,6 @@ class WeakMap
// Resolve ambiguity with LinkedListElement<>::remove.
using Base::remove;
using UnbarrieredKey = typename detail::RemoveBarrier<Key>::Type;
explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr);
// Add a read barrier to prevent an incorrectly gray value from escaping the
@ -205,8 +173,6 @@ class WeakMap
return p;
}
Ptr unbarrieredLookup(const Lookup& l) const { return Base::lookup(l); }
AddPtr lookupForAdd(const Lookup& l) {
AddPtr p = Base::lookupForAdd(l);
if (p) {
@ -215,111 +181,34 @@ class WeakMap
return p;
}
void remove(Ptr p) {
MOZ_ASSERT(p.found());
if (markColor != CellColor::White) {
forgetKey(p->key());
}
Base::remove(p);
}
void remove(const Lookup& l) {
if (Ptr p = lookup(l)) {
remove(p);
}
}
void clear() {
Base::clear();
JSRuntime* rt = zone()->runtimeFromMainThread();
if (zone()->needsIncrementalBarrier()) {
rt->gc.marker.forgetWeakMap(this, zone());
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool put(KeyInput&& key, ValueInput&& value) {
MOZ_ASSERT(key);
return Base::put(std::forward<KeyInput>(key),
std::forward<ValueInput>(value));
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
if (!Base::add(p, std::forward<KeyInput>(k), std::forward<ValueInput>(v))) {
return false;
}
barrierForInsert(p->key(), p->value());
return true;
MOZ_MUST_USE bool putNew(KeyInput&& key, ValueInput&& value) {
MOZ_ASSERT(key);
return Base::putNew(std::forward<KeyInput>(key),
std::forward<ValueInput>(value));
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
if (!Base::relookupOrAdd(p, std::forward<KeyInput>(k),
std::forward<ValueInput>(v))) {
return false;
}
barrierForInsert(p->key(), p->value());
return true;
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool put(KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
AddPtr p = lookupForAdd(k);
if (p) {
p->value() = std::forward<ValueInput>(v);
return true;
}
return add(p, std::forward<KeyInput>(k), std::forward<ValueInput>(v));
}
template <typename KeyInput, typename ValueInput>
MOZ_MUST_USE bool putNew(KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
barrierForInsert(k, v);
return Base::putNew(std::forward<KeyInput>(k), std::forward<ValueInput>(v));
}
template <typename KeyInput, typename ValueInput>
void putNewInfallible(KeyInput&& k, ValueInput&& v) {
MOZ_ASSERT(k);
barrierForInsert(k, v);
Base::putNewInfallible(std::forward(k), std::forward<KeyInput>(k));
MOZ_MUST_USE bool relookupOrAdd(AddPtr& ptr, KeyInput&& key,
ValueInput&& value) {
MOZ_ASSERT(key);
return Base::relookupOrAdd(ptr, std::forward<KeyInput>(key),
std::forward<ValueInput>(value));
}
void markEntry(GCMarker* marker, gc::Cell* markedCell,
gc::Cell* origKey) override;
// 'key' has lost its delegate, update our weak key state.
void postSeverDelegate(GCMarker* marker, gc::Cell* key) override;
void trace(JSTracer* trc) override;
protected:
void forgetKey(UnbarrieredKey key) {
// Remove the key or its delegate from weakKeys.
JSRuntime* rt = zone()->runtimeFromMainThread();
if (rt->gc.isIncrementalGCInProgress()) {
if (JSObject* delegate = getDelegate(key)) {
js::gc::WeakKeyTable& weakKeys = delegate->zone()->gcWeakKeys(delegate);
rt->gc.marker.forgetWeakKey(weakKeys, this, delegate, key);
} else {
js::gc::WeakKeyTable& weakKeys = key->zone()->gcWeakKeys(key);
rt->gc.marker.forgetWeakKey(weakKeys, this, key, key);
}
}
}
void barrierForInsert(Key k, const Value& v) {
if (markColor == CellColor::White) {
return;
}
if (!zone()->needsIncrementalBarrier()) {
return;
}
JSTracer* trc = zone()->barrierTracer();
Value tmp = v;
TraceEdge(trc, &tmp, "weakmap inserted value");
MOZ_ASSERT(tmp == v);
}
// We have a key that, if it or its delegate is marked, may lead to a WeakMap
// value getting marked. Insert it or its delegate (if any) into the
// appropriate zone's gcWeakKeys or gcNurseryWeakKeys.
@ -345,9 +234,9 @@ class WeakMap
JS::ExposeObjectToActiveJS(obj);
}
CellColor getDelegateColor(JSObject* key) const;
CellColor getDelegateColor(JSScript* script) const;
CellColor getDelegateColor(LazyScript* script) const;
bool keyNeedsMark(GCMarker* marker, JSObject* key) const;
bool keyNeedsMark(GCMarker* marker, JSScript* script) const;
bool keyNeedsMark(GCMarker* marker, LazyScript* script) const;
bool findZoneEdges() override {
// This is overridden by ObjectValueMap.

Просмотреть файл

@ -399,13 +399,6 @@ void Zone::discardJitCode(FreeOp* fop,
jitZone()->cfgSpace()->lifoAlloc().freeAll();
}
void JS::Zone::delegatePreWriteBarrierInternal(JSObject* obj,
JSObject* delegate) {
MOZ_ASSERT(js::WeakMapBase::getDelegate(obj) == delegate);
MOZ_ASSERT(needsIncrementalBarrier());
GCMarker::fromTracer(barrierTracer())->severWeakDelegate(obj, delegate);
}
#ifdef JSGC_HASH_TABLE_CHECKS
void JS::Zone::checkUniqueIdTableAfterMovingGC() {
for (auto r = uniqueIds().all(); !r.empty(); r.popFront()) {

Просмотреть файл

@ -404,12 +404,6 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
weakCaches().insertBack(cachep);
}
void delegatePreWriteBarrier(JSObject* obj, JSObject* delegate) {
if (needsIncrementalBarrier()) {
delegatePreWriteBarrierInternal(obj, delegate);
}
}
private:
/*
* Mapping from not yet marked keys to a vector of all values that the key
@ -420,14 +414,9 @@ class Zone : public js::ZoneAllocator, public js::gc::GraphNodeBase<JS::Zone> {
js::ZoneOrGCTaskData<js::gc::WeakKeyTable> gcNurseryWeakKeys_;
public:
void delegatePreWriteBarrierInternal(JSObject* obj, JSObject* delegate);
js::gc::WeakKeyTable& gcWeakKeys() { return gcWeakKeys_.ref(); }
js::gc::WeakKeyTable& gcNurseryWeakKeys() { return gcNurseryWeakKeys_.ref(); }
js::gc::WeakKeyTable& gcWeakKeys(const js::gc::Cell* cell) {
return cell->isTenured() ? gcWeakKeys() : gcNurseryWeakKeys();
}
// A set of edges from this zone to other zones used during GC to calculate
// sweep groups.
NodeSet& gcSweepGroupEdges() {

Просмотреть файл

@ -1,19 +0,0 @@
// |jit-test| skip-if: !('oomTest' in this)
// Adapted from randomly chosen test: js/src/jit-test/tests/debug/Memory-drainAllocationsLog-13.js
//
// This triggers OOMs that will cause weak marking mode to abort.
const root = newGlobal({
newCompartment: true
});
root.eval("dbg = new Debugger()");
root.dbg.addDebuggee(this);
root.dbg.memory.trackingAllocationSites = true;
// jsfunfuzz-generated
relazifyFunctions('compartment');
print(/x/);
oomTest((function() {
String.prototype.localeCompare()
}), {
keepFailing: true
});

Просмотреть файл

@ -17,7 +17,7 @@ function basicSweeping() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(wm1.get(hold).name, 'val2');
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
@ -41,7 +41,7 @@ function weakGraph() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(obj2.name, "obj2");
assertEq(wm1.get(obj2).name, "obj3");
@ -70,7 +70,7 @@ function deadWeakMap() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(obj2.name, "obj2");
assertEq(finalizeCount(), initialCount + 1);
@ -98,7 +98,7 @@ function deadKeys() {
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(finalizeCount(), initialCount + 2);
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0);
@ -132,7 +132,7 @@ function weakKeysRealloc() {
var initialCount = finalizeCount();
finishgc();
startgc(100000, 'shrinking');
finishgc();
gcslice();
assertEq(finalizeCount(), initialCount + 1);
}

Просмотреть файл

@ -171,56 +171,6 @@ function nukeMarking() {
if (this.enqueueMark)
runtest(nukeMarking);
// Similar to the above, but try to trigger a rehash of the weak keys table for
// a zone, which previously invalidated an iterator (bug 1556430).
function nukeMarking2() {
// Create a global in a different compartment, same zone, so the delegate and
// key can live in the same zone.
const g1 = newGlobal({sameZoneAs: this});
let vals = { maps: [], keys: [] };
for (const i of [,,,,,]) {
vals.maps.push(new WeakMap());
vals.keys.push(g1.eval("Object.create(null)"));
}
vals.val = Object.create(null);
for (const i in vals.maps) {
vals.maps[i].set(vals.keys[i], vals.val);
}
vals.val = null;
gc();
// Set up the sequence of marking events.
for (const map of vals.maps) {
enqueueMark(map);
}
enqueueMark("yield");
// We will nuke the key's delegate here.
for (const key of vals.keys) {
enqueueMark(key);
}
addMarkObservers([vals.keys[0]]);
enqueueMark("enter-weak-marking-mode");
vals = null;
// Okay, run through the GC now.
startgc(1000000);
assertEq(gcstate(), "Mark", "expected to yield after marking map");
print(getMarks()[0]);
const q = getMarkQueue();
// We should have marked the map and then yielded back here.
nukeCCW(q[q.length - 2]);
// Finish up the GC.
gcslice();
clearMarkQueue();
clearMarkObservers();
}
if (this.enqueueMark)
nukeMarking2();
function transplantMarking() {
const g1 = newGlobal({newCompartment: true});

Просмотреть файл

@ -27,7 +27,6 @@
#include "gc/GC-inl.h"
#include "gc/Marking-inl.h"
#include "gc/WeakMap-inl.h"
#include "vm/JSAtom-inl.h"
#include "vm/JSFunction-inl.h"
#include "vm/JSObject-inl.h"
@ -77,18 +76,6 @@ bool Compartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
return true;
}
void Compartment::removeWrapper(js::WrapperMap::Ptr p) {
if (p->key().is<JSObject*>()) {
JSObject* key = p->key().as<JSObject*>();
JS::Value value = p->value().unbarrieredGet();
if (js::WeakMapBase::getDelegate(&value.toObject()) == key) {
key->zone()->delegatePreWriteBarrier(&value.toObject(), key);
}
}
crossCompartmentWrappers.remove(p);
}
static JSString* CopyStringPure(JSContext* cx, JSString* str) {
/*
* Directly allocate the copy in the destination compartment, rather than

Просмотреть файл

@ -602,8 +602,6 @@ class JS::Compartment {
const js::CrossCompartmentKey& wrapped,
const js::Value& wrapper);
void removeWrapper(js::WrapperMap::Ptr p);
js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) const {
return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(wrapped));
}
@ -612,6 +610,10 @@ class JS::Compartment {
return crossCompartmentWrappers.lookup(js::CrossCompartmentKey(obj));
}
void removeWrapper(js::WrapperMap::Ptr p) {
crossCompartmentWrappers.remove(p);
}
bool hasNurseryAllocatedWrapperEntries(const js::CompartmentFilter& f) {
return crossCompartmentWrappers.hasNurseryAllocatedWrapperEntries(f);
}

Просмотреть файл

@ -12,7 +12,6 @@
#include "vm/Realm.h"
#include "gc/ObjectKind-inl.h"
#include "gc/WeakMap-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/TypeInference-inl.h"
@ -152,15 +151,6 @@ inline void ProxyObject::setPrivate(const Value& priv) {
}
void ProxyObject::nuke() {
// Notify the zone that a delegate is no longer a delegate. Be careful not to
// expose this pointer, because it has already been removed from the wrapper
// map yet we have assertions during tracing that will verify that it is
// still present.
JSObject* delegate = UncheckedUnwrapWithoutExpose(this);
if (delegate != this) {
delegate->zone()->delegatePreWriteBarrier(this, delegate);
}
// Clear the target reference and replaced it with a value that encodes
// various information about the original target.
setSameCompartmentPrivate(DeadProxyTargetValue(this));

Просмотреть файл

@ -488,8 +488,6 @@ class JS::Realm : public JS::shadow::Realm {
// can easily lead to races. Use this method very carefully.
JSRuntime* runtimeFromAnyThread() const { return runtime_; }
void removeWrapper(js::WrapperMap::Ptr p);
const JS::RealmCreationOptions& creationOptions() const {
return creationOptions_;
}

Просмотреть файл

@ -4099,13 +4099,13 @@ nsresult nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext,
#ifdef MOZ_DUMP_PAINTING
gfxUtils::sDumpPaintFile = savedDumpFile;
#endif
if (StaticPrefs::DumpClientLayers()) {
std::stringstream ss;
FrameLayerBuilder::DumpRetainedLayerTree(layerManager, ss, false);
print_stderr(ss);
}
#endif
// Update the widget's opaque region information. This sets
// glass boundaries on Windows. Also set up the window dragging region

Просмотреть файл

@ -0,0 +1,16 @@
<html>
<style>
html {
scrollbar-width: none;
}
body {
margin: 0;
}
div {
width: 200px;
height: 200px;
background: green;
}
</style>
<div></div>
</html>

Просмотреть файл

@ -0,0 +1,16 @@
<html reftest-resolution="2.0">
<style>
html {
scrollbar-width: none;
}
body {
margin: 0;
}
div {
width: 100px;
height: 100px;
background: green;
}
</style>
<div></div>
</html>

Просмотреть файл

@ -205,3 +205,6 @@ fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-1.html about:blank
fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-2.html about:blank
fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-3.html about:blank
fails-if(layerChecksEnabled) != reftest-assigned-layer-fail-4.html about:blank
# reftest-resolution
pref(apz.allow_zooming,true) == reftest-resolution.html reftest-resolution-ref.html

Просмотреть файл

@ -588,6 +588,18 @@ the root element are non-zero, sets the scroll-position-clamping scroll-port
size to the given size in CSS pixels. This does not affect the size of the
snapshot that is taken.
Setting Resolution: reftest-resolution="<float>"
================================================
If the root element of a test has a "reftest-resolution" attribute, the page
is rendered with the specified resolution (as if the user pinch-zoomed in
to that scale). Note that the difference between reftest-async-zoom and
reftest-resolution is that reftest-async-zoom only applies the scale in
the compositor, while reftest-resolution causes the page to be paint at that
resolution.
This attributes requires the pref apz.allow_zooming=true to have an effect.
Setting Async Scroll Mode: reftest-async-scroll attribute
=========================================================

Просмотреть файл

@ -299,7 +299,11 @@ function setupViewport(contentRootElement) {
windowUtils().setVisualViewportSize(sw, sh);
}
// XXX support resolution when needed
var res = attrOrDefault(contentRootElement, "reftest-resolution", 1);
if (res !== 1) {
LogInfo("Setting resolution to " + res);
windowUtils().setResolutionAndScaleTo(res);
}
// XXX support viewconfig when needed
}

Просмотреть файл

@ -3583,15 +3583,16 @@ VARCACHE_PREF(
#ifdef MOZ_DUMP_PAINTING
VARCACHE_PREF(
Live,
"layers.dump-client-layers",
DumpClientLayers,
"layers.dump-decision",
LayersDumpDecision,
RelaxedAtomicBool, false
)
#endif
VARCACHE_PREF(
Live,
"layers.dump-decision",
LayersDumpDecision,
"layers.dump-client-layers",
DumpClientLayers,
RelaxedAtomicBool, false
)
@ -3601,7 +3602,6 @@ VARCACHE_PREF(
DumpHostLayers,
RelaxedAtomicBool, false
)
#endif
// 0 is "no change" for contrast, positive values increase it, negative values
// decrease it until we hit mid gray at -1 contrast, after that it gets weird.

Просмотреть файл

@ -30,6 +30,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'gtest',
]
OS_LIBS += [
'mincore',
'ntdll',
'version',
]

Просмотреть файл

@ -32,6 +32,8 @@ skip-if = os != 'win'
[TestMacroForEach]
[TestMathAlgorithms]
[TestMaybe]
[TestNativeNt]
skip-if = os != 'win'
[TestBaseProfiler]
[TestNonDereferenceable]
[TestNotNull]

Просмотреть файл

@ -12,7 +12,6 @@
#include "mozilla/Poison.h"
#include "mozilla/RemoteDecoderManagerChild.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/VideoDecoderManagerChild.h"
#include "mozilla/XPCOM.h"
#include "mozJSComponentLoader.h"
#include "nsXULAppAPI.h"
@ -624,7 +623,6 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
// are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
NS_ProcessPendingEvents(thread);
gfxPlatform::ShutdownLayersIPC();
mozilla::VideoDecoderManagerChild::Shutdown();
mozilla::RemoteDecoderManagerChild::Shutdown();
mozilla::scache::StartupCache::DeleteSingleton();