зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
d957131916
|
@ -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,6 +84,34 @@ void RemoteDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
|
|||
MOZ_ASSERT(mCanSend);
|
||||
// If the IPC channel is gone pending promises need to be resolved/rejected.
|
||||
if (aWhy == AbnormalShutdown) {
|
||||
// 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__);
|
||||
|
@ -88,7 +120,9 @@ void RemoteDecoderChild::ActorDestroy(ActorDestroyReason aWhy) {
|
|||
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__);
|
||||
}
|
||||
|
||||
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,10 +161,23 @@ 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) {
|
||||
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
|
||||
// MediaDataDecoder so that the demuxer could write directly
|
||||
|
@ -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,12 +75,18 @@ void RemoteDecoderManagerChild::Shutdown() {
|
|||
|
||||
if (sRemoteDecoderManagerChildThread) {
|
||||
sRemoteDecoderManagerChildThread->Dispatch(
|
||||
NS_NewRunnableFunction("dom::RemoteDecoderManagerChild::Shutdown",
|
||||
NS_NewRunnableFunction(
|
||||
"dom::RemoteDecoderManagerChild::Shutdown",
|
||||
[]() {
|
||||
if (sRemoteDecoderManagerChild &&
|
||||
sRemoteDecoderManagerChild->CanSend()) {
|
||||
sRemoteDecoderManagerChild->Close();
|
||||
sRemoteDecoderManagerChild = nullptr;
|
||||
if (sRemoteDecoderManagerChildForRDDProcess &&
|
||||
sRemoteDecoderManagerChildForRDDProcess->CanSend()) {
|
||||
sRemoteDecoderManagerChildForRDDProcess->Close();
|
||||
sRemoteDecoderManagerChildForRDDProcess = nullptr;
|
||||
}
|
||||
if (sRemoteDecoderManagerChildForGPUProcess &&
|
||||
sRemoteDecoderManagerChildForGPUProcess->CanSend()) {
|
||||
sRemoteDecoderManagerChildForGPUProcess->Close();
|
||||
sRemoteDecoderManagerChildForGPUProcess = nullptr;
|
||||
}
|
||||
}),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
@ -62,13 +94,35 @@ void RemoteDecoderManagerChild::Shutdown() {
|
|||
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,6 +332,27 @@ MediaResult RemoteVideoDecoderParent::ProcessDecodedData(
|
|||
"Decoded video must output a layer::Image to "
|
||||
"be used with RemoteDecoderParent");
|
||||
|
||||
SurfaceDescriptor sd;
|
||||
IntSize size;
|
||||
|
||||
if (mKnowsCompositor) {
|
||||
RefPtr<TextureClient> texture =
|
||||
video->mImage->GetTextureClient(mKnowsCompositor);
|
||||
|
||||
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());
|
||||
|
||||
|
@ -201,10 +373,14 @@ MediaResult 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;
|
||||
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,19 +2509,19 @@ 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.
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
|
@ -2585,69 +2529,30 @@ void GCMarker::enterWeakMarkingMode() {
|
|||
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;
|
||||
for (SweepGroupZonesIter zone(runtime()); !zone.done(); zone.next()) {
|
||||
for (WeakMapBase* m : zone->gcWeakMapList()) {
|
||||
if (m->marked) {
|
||||
(void)m->markEntries(this);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
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
|
||||
}
|
||||
}
|
||||
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(
|
||||
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker,
|
||||
JSScript* script) const {
|
||||
return CellColor::White;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
inline WeakMapBase::CellColor WeakMap<K, V>::getDelegateColor(
|
||||
inline bool WeakMap<K, V>::keyNeedsMark(GCMarker* marker,
|
||||
LazyScript* script) const {
|
||||
return CellColor::White;
|
||||
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)) {
|
||||
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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче