diff --git a/dom/media/MediaData.cpp b/dom/media/MediaData.cpp index 9f1205a0e1..e770fa1449 100644 --- a/dom/media/MediaData.cpp +++ b/dom/media/MediaData.cpp @@ -9,9 +9,16 @@ #include "ImageContainer.h" #include "mozilla/layers/SharedRGBImage.h" #include "YCbCrUtils.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/KnowsCompositor.h" #include +#ifdef XP_WIN +#include "mozilla/WindowsVersion.h" +#include "mozilla/layers/D3D11YCbCrImage.h" +#endif + namespace mozilla { using namespace mozilla::gfx; @@ -218,19 +225,14 @@ VideoData::ShallowCopyUpdateTimestampAndDuration(const VideoData* aOther, return v.forget(); } -/* static */ -bool VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage, - const VideoInfo& aInfo, - const YCbCrBuffer &aBuffer, - const IntRect& aPicture, - bool aCopyData) +PlanarYCbCrData +ConstructPlanarYCbCrData(const VideoInfo& aInfo, + const VideoData::YCbCrBuffer& aBuffer, + const IntRect& aPicture) { - if (!aVideoImage) { - return false; - } - const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0]; - const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1]; - const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2]; + const VideoData::YCbCrBuffer::Plane& Y = aBuffer.mPlanes[0]; + const VideoData::YCbCrBuffer::Plane& Cb = aBuffer.mPlanes[1]; + const VideoData::YCbCrBuffer::Plane& Cr = aBuffer.mPlanes[2]; PlanarYCbCrData data; data.mYChannel = Y.mData + Y.mOffset; @@ -249,6 +251,21 @@ bool VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage, data.mStereoMode = aInfo.mStereoMode; data.mYUVColorSpace = aBuffer.mYUVColorSpace; data.mColorRange = aBuffer.mColorRange; + return data; +} + +/* static */ bool +VideoData::SetVideoDataToImage(PlanarYCbCrImage* aVideoImage, + const VideoInfo& aInfo, + const YCbCrBuffer &aBuffer, + const IntRect& aPicture, + bool aCopyData) +{ + if (!aVideoImage) { + return false; + } + + PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture); aVideoImage->SetDelayedConversion(true); if (aCopyData) { @@ -268,7 +285,8 @@ VideoData::CreateAndCopyData(const VideoInfo& aInfo, const YCbCrBuffer& aBuffer, bool aKeyframe, int64_t aTimecode, - const IntRect& aPicture) + const IntRect& aPicture, + layers::KnowsCompositor* aAllocator) { if (!aContainer) { // Create a dummy VideoData with no image. This gives us something to @@ -296,6 +314,23 @@ VideoData::CreateAndCopyData(const VideoInfo& aInfo, 0)); // Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR // format. +#if XP_WIN + // We disable this code path on Windows 7 due to intermittent crashes with old drivers. + // See Mozilla bug 1405110. + if (IsWin8OrLater() && !XRE_IsParentProcess() && + aAllocator && aAllocator->GetCompositorBackendType() + == layers::LayersBackend::LAYERS_D3D11) { + RefPtr d3d11Image = new layers::D3D11YCbCrImage(); + PlanarYCbCrData data = ConstructPlanarYCbCrData(aInfo, aBuffer, aPicture); + if (d3d11Image->SetData(layers::ImageBridgeChild::GetSingleton() + ? layers::ImageBridgeChild::GetSingleton().get() + : aAllocator, + aContainer, data)) { + v->mImage = d3d11Image; + return v.forget(); + } + } +#endif if (!v->mImage) { v->mImage = aContainer->CreatePlanarYCbCrImage(); } diff --git a/dom/media/MediaData.h b/dom/media/MediaData.h index 02a7162f42..4fe8dbc5f2 100644 --- a/dom/media/MediaData.h +++ b/dom/media/MediaData.h @@ -25,6 +25,7 @@ namespace mozilla { namespace layers { class Image; class ImageContainer; +class KnowsCompositor; } // namespace layers class MediaByteBuffer; @@ -472,7 +473,8 @@ public: const YCbCrBuffer &aBuffer, bool aKeyframe, int64_t aTimecode, - const IntRect& aPicture); + const IntRect& aPicture, + layers::KnowsCompositor* aAllocator = nullptr); static already_AddRefed CreateAndCopyData(const VideoInfo& aInfo, ImageContainer* aContainer, diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 24c29599f0..fac74eb6a9 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -1500,6 +1500,15 @@ MediaFormatReader::Update(TrackType aTrack) nsCString error; mVideo.mIsHardwareAccelerated = mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error); +#ifdef XP_WIN + // D3D11_YCBCR_IMAGE images are GPU based, we try to limit the amount + // of GPU RAM used. + VideoData* videoData = static_cast(output.get()); + mVideo.mIsHardwareAccelerated = + mVideo.mIsHardwareAccelerated || + (videoData->mImage && + videoData->mImage->GetFormat() == ImageFormat::D3D11_YCBCR_IMAGE); +#endif } } else if (decoder.HasFatalError()) { LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack)); diff --git a/dom/media/platforms/agnostic/TheoraDecoder.cpp b/dom/media/platforms/agnostic/TheoraDecoder.cpp index de715df876..2075d9aa9f 100644 --- a/dom/media/platforms/agnostic/TheoraDecoder.cpp +++ b/dom/media/platforms/agnostic/TheoraDecoder.cpp @@ -38,7 +38,8 @@ ogg_packet InitTheoraPacket(const unsigned char* aData, size_t aLength, } TheoraDecoder::TheoraDecoder(const CreateDecoderParams& aParams) - : mImageContainer(aParams.mImageContainer) + : mImageAllocator(aParams.mKnowsCompositor) + , mImageContainer(aParams.mImageContainer) , mTaskQueue(aParams.mTaskQueue) , mCallback(aParams.mCallback) , mIsFlushing(false) @@ -176,7 +177,8 @@ TheoraDecoder::DoDecode(MediaRawData* aSample) aSample->mKeyframe, aSample->mTimecode, mInfo.ScaledImageRect(mTheoraInfo.frame_width, - mTheoraInfo.frame_height)); + mTheoraInfo.frame_height), + mImageAllocator); if (!v) { LOG("Image allocation error source %ldx%ld display %ldx%ld picture %ldx%ld", mTheoraInfo.frame_width, mTheoraInfo.frame_height, mInfo.mDisplay.width, mInfo.mDisplay.height, diff --git a/dom/media/platforms/agnostic/TheoraDecoder.h b/dom/media/platforms/agnostic/TheoraDecoder.h index 9181989be8..7af6d27bc0 100644 --- a/dom/media/platforms/agnostic/TheoraDecoder.h +++ b/dom/media/platforms/agnostic/TheoraDecoder.h @@ -43,6 +43,7 @@ private: MediaResult DoDecode(MediaRawData* aSample); void ProcessDrain(); + RefPtr mImageAllocator; RefPtr mImageContainer; RefPtr mTaskQueue; MediaDataDecoderCallback* mCallback; diff --git a/dom/media/platforms/agnostic/VPXDecoder.cpp b/dom/media/platforms/agnostic/VPXDecoder.cpp index 00cf7d85cc..9479b89251 100644 --- a/dom/media/platforms/agnostic/VPXDecoder.cpp +++ b/dom/media/platforms/agnostic/VPXDecoder.cpp @@ -69,6 +69,7 @@ InitContext(vpx_codec_ctx_t* aCtx, VPXDecoder::VPXDecoder(const CreateDecoderParams& aParams) : mImageContainer(aParams.mImageContainer) + , mImageAllocator(aParams.mKnowsCompositor) , mTaskQueue(aParams.mTaskQueue) , mCallback(aParams.mCallback) , mIsFlushing(false) @@ -149,7 +150,7 @@ VPXDecoder::DoDecode(MediaRawData* aSample) "WebM image format not I420 or I444"); NS_ASSERTION(!alpha_decoded, "Multiple frames per packet that contains alpha"); - + if (aSample->AlphaSize() > 0) { if(!alpha_decoded){ MediaResult rv = DecodeAlpha(&img_alpha, aSample); @@ -221,7 +222,8 @@ VPXDecoder::DoDecode(MediaRawData* aSample) aSample->mKeyframe, aSample->mTimecode, mInfo.ScaledImageRect(img->d_w, - img->d_h)); + img->d_h), + mImageAllocator); } else { VideoData::YCbCrBuffer::Plane alpha_plane; alpha_plane.mData = img_alpha->planes[0]; diff --git a/dom/media/platforms/agnostic/VPXDecoder.h b/dom/media/platforms/agnostic/VPXDecoder.h index 25d9bc1d33..e2686dd02f 100644 --- a/dom/media/platforms/agnostic/VPXDecoder.h +++ b/dom/media/platforms/agnostic/VPXDecoder.h @@ -61,6 +61,7 @@ private: MediaRawData* aSample); const RefPtr mImageContainer; + RefPtr mImageAllocator; const RefPtr mTaskQueue; MediaDataDecoderCallback* mCallback; Atomic mIsFlushing; diff --git a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h index 95b156ff96..c6536da5a1 100644 --- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h +++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h @@ -44,6 +44,7 @@ public: aParams.mTaskQueue, aParams.mCallback, aParams.VideoConfig(), + aParams.mKnowsCompositor, aParams.mImageContainer); return decoder.forget(); } diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp index b2d656a4e1..9056b7c704 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp @@ -11,6 +11,7 @@ #include "MediaInfo.h" #include "VPXDecoder.h" #include "MP4Decoder.h" +#include "mozilla/layers/KnowsCompositor.h" #include "FFmpegVideoDecoder.h" #include "FFmpegLog.h" @@ -109,8 +110,9 @@ FFmpegVideoDecoder::PtsCorrectionContext::Reset() FFmpegVideoDecoder::FFmpegVideoDecoder(FFmpegLibWrapper* aLib, TaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, const VideoInfo& aConfig, - ImageContainer* aImageContainer) + KnowsCompositor* aAllocator, ImageContainer* aImageContainer) : FFmpegDataDecoder(aLib, aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType)) + , mImageAllocator(aAllocator) , mImageContainer(aImageContainer) , mInfo(aConfig) , mCodecParser(nullptr) @@ -402,7 +404,8 @@ FFmpegVideoDecoder::CreateImage(int64_t aOffset, int64_t aPts, !!mFrame->key_frame, -1, mInfo.ScaledImageRect(mFrame->width, - mFrame->height)); + mFrame->height), + mImageAllocator); if (!v) { return MediaResult(NS_ERROR_OUT_OF_MEMORY, diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h index adf331ce49..f58ca9d7b0 100644 --- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h +++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h @@ -24,11 +24,13 @@ class FFmpegVideoDecoder : public FFmpegDataDecoder { typedef mozilla::layers::Image Image; typedef mozilla::layers::ImageContainer ImageContainer; + typedef mozilla::layers::KnowsCompositor KnowsCompositor; public: FFmpegVideoDecoder(FFmpegLibWrapper* aLib, TaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback, const VideoInfo& aConfig, + KnowsCompositor* aAllocator, ImageContainer* aImageContainer); virtual ~FFmpegVideoDecoder(); @@ -62,6 +64,7 @@ private: int AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext, AVFrame* aFrame); + RefPtr mImageAllocator; RefPtr mImageContainer; VideoInfo mInfo; diff --git a/dom/script/ModuleScript.cpp b/dom/script/ModuleScript.cpp index 425da8207a..a349bd5560 100644 --- a/dom/script/ModuleScript.cpp +++ b/dom/script/ModuleScript.cpp @@ -53,29 +53,41 @@ LoadedScript::LoadedScript(ScriptKind aKind, LoadedScript::~LoadedScript() { DropJSObjects(this); } void LoadedScript::AssociateWithScript(JSScript* aScript) { - // Set a JSScript's private value to point to this object and - // increment our reference count. This is decremented by - // HostFinalizeTopLevelScript() below when the JSScript dies. + // Set a JSScript's private value to point to this object. The JS engine will + // increment our reference count by calling HostAddRefTopLevelScript(). This + // is decremented by HostReleaseTopLevelScript() below when the JSScript dies. MOZ_ASSERT(JS::GetScriptPrivate(aScript).isUndefined()); JS::SetScriptPrivate(aScript, JS::PrivateValue(this)); - AddRef(); } -void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate) { - // Decrement the reference count of a LoadedScript object that is - // pointed to by a dying JSScript. The reference count was - // originally incremented by AssociateWithScript() above. - - auto script = static_cast(aPrivate.toPrivate()); - +inline void CheckModuleScriptPrivate(LoadedScript* script, + const JS::Value& aPrivate) { #ifdef DEBUG if (script->IsModuleScript()) { JSObject* module = script->AsModuleScript()->mModuleRecord.unbarrieredGet(); MOZ_ASSERT_IF(module, JS::GetModulePrivate(module) == aPrivate); } #endif +} +void HostAddRefTopLevelScript(const JS::Value& aPrivate) { + // Increment the reference count of a LoadedScript object that is now pointed + // to by a JSScript. The reference count is decremented by + // HostReleaseTopLevelScript() below. + + auto script = static_cast(aPrivate.toPrivate()); + CheckModuleScriptPrivate(script, aPrivate); + script->AddRef(); +} + +void HostReleaseTopLevelScript(const JS::Value& aPrivate) { + // Decrement the reference count of a LoadedScript object that was pointed to + // by a JSScript. The reference count was originally incremented by + // HostAddRefTopLevelScript() above. + + auto script = static_cast(aPrivate.toPrivate()); + CheckModuleScriptPrivate(script, aPrivate); script->Release(); } @@ -132,7 +144,6 @@ ModuleScript::UnlinkModuleRecord() this); JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue()); mModuleRecord = nullptr; - Release(); } } @@ -151,13 +162,13 @@ ModuleScript::SetModuleRecord(JS::Handle aModuleRecord) mModuleRecord = aModuleRecord; - // Make module's host defined field point to this object and - // increment our reference count. This is decremented by - // UnlinkModuleRecord() above. + // Make module's host defined field point to this object. The JS engine will + // increment our reference count by calling HostAddRefTopLevelScript(). This + // is decremented when the field is cleared in UnlinkModuleRecord() above or + // when the module record dies. MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).isUndefined()); JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this)); HoldJSObjects(this); - AddRef(); } void diff --git a/dom/script/ModuleScript.h b/dom/script/ModuleScript.h index b3eb841778..64b16db67d 100644 --- a/dom/script/ModuleScript.h +++ b/dom/script/ModuleScript.h @@ -18,7 +18,8 @@ namespace dom { class ScriptLoader; -void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate); +void HostAddRefTopLevelScript(const JS::Value& aPrivate); +void HostReleaseTopLevelScript(const JS::Value& aPrivate); class ClassicScript; class ModuleScript; @@ -63,7 +64,6 @@ class ClassicScript final : public LoadedScript class ModuleScript final : public LoadedScript { - JS::Heap mModuleRecord; JS::Heap mParseError; JS::Heap mErrorToRethrow; @@ -74,6 +74,8 @@ public: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleScript, LoadedScript) + JS::Heap mModuleRecord; + ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL); void SetModuleRecord(JS::Handle aModuleRecord); @@ -89,7 +91,7 @@ public: void UnlinkModuleRecord(); - friend void HostFinalizeTopLevelScript(JSFreeOp*, const JS::Value&); + friend void CheckModuleScriptPrivate(LoadedScript*, const JS::Value&); }; ClassicScript* LoadedScript::AsClassicScript() { diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index 70b48f2c4f..0750e03db7 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -185,7 +185,11 @@ ScriptLoadRequest::MaybeCancelOffThreadScript() } JSContext* cx = danger::GetJSContext(); - JS::CancelOffThreadScript(cx, mOffThreadToken); + if (IsModuleRequest()) { + JS::CancelOffThreadModule(cx, mOffThreadToken); + } else { + JS::CancelOffThreadScript(cx, mOffThreadToken); + } mOffThreadToken = nullptr; } @@ -1109,8 +1113,9 @@ void ScriptLoader::EnsureModuleHooksInitialized() { JS::SetModuleResolveHook(rt, HostResolveImportedModule); JS::SetModuleMetadataHook(jsapi.cx(), HostPopulateImportMeta); - JS::SetScriptPrivateFinalizeHook(jsapi.cx(), HostFinalizeTopLevelScript); - + JS::SetScriptPrivateReferenceHooks(jsapi.cx(), HostAddRefTopLevelScript, + HostReleaseTopLevelScript); + Preferences::RegisterCallbackAndCall(DynamicImportPrefChangedCallback, "javascript.options.dynamicImport", (void*)nullptr); @@ -2648,7 +2653,7 @@ ScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, // Process our request and/or any pending ones ProcessPendingRequests(); - return NS_OK; + return rv; } nsresult @@ -2758,6 +2763,9 @@ ScriptLoader::HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult) { if (aRequest->isInList()) { RefPtr req = mDynamicImportRequests.Steal(aRequest); modReq->Cancel(); + // FinishDynamicImport must happen exactly once for each dynamic import + // request. If the load is aborted we do it when we remove the request + // from mDynamicImportRequests. FinishDynamicImport(modReq, aResult); } } else { @@ -2979,8 +2987,12 @@ ScriptLoader::ParsingComplete(bool aTerminated) for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req; req = req->getNext()) { req->Cancel(); + // FinishDynamicImport must happen exactly once for each dynamic import + // request. If the load is aborted we do it when we remove the request + // from mDynamicImportRequests. FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT); } + mDynamicImportRequests.Clear(); if (mParserBlockingRequest) { mParserBlockingRequest->Cancel(); diff --git a/dom/svg/SVGUseElement.cpp b/dom/svg/SVGUseElement.cpp index 8da90634ad..acd8941b4e 100644 --- a/dom/svg/SVGUseElement.cpp +++ b/dom/svg/SVGUseElement.cpp @@ -426,17 +426,6 @@ SVGUseElement::LookupHref() nsCOMPtr targetURI; nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, GetComposedDoc(), baseURI); - - // Do not allow 'data:' schemes in elements. - // See spec update: https://github.com/w3c/svgwg/pull/901 - if (targetURI) { - bool isData; - mozilla::Unused << targetURI->SchemeIs("data", &isData); - if (isData) { - return; - } - } - mSource.Reset(this, targetURI); } diff --git a/gfx/layers/D3D11YCbCrImage.cpp b/gfx/layers/D3D11YCbCrImage.cpp new file mode 100644 index 0000000000..de2d209d28 --- /dev/null +++ b/gfx/layers/D3D11YCbCrImage.cpp @@ -0,0 +1,335 @@ +/* -*- Mode: C++; tab-width: 20; 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 "D3D11YCbCrImage.h" + +#include "gfx2DGlue.h" +#include "YCbCrUtils.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/layers/CompositableClient.h" +#include "mozilla/layers/CompositableForwarder.h" +#include "mozilla/layers/TextureD3D11.h" + +using namespace mozilla::gfx; + +namespace mozilla { +namespace layers { + +D3D11YCbCrImage::D3D11YCbCrImage() + : Image(NULL, ImageFormat::D3D11_YCBCR_IMAGE) +{ +} + +D3D11YCbCrImage::~D3D11YCbCrImage() { } + +bool +D3D11YCbCrImage::SetData(KnowsCompositor* aAllocator, + ImageContainer* aContainer, + const PlanarYCbCrData& aData) +{ + mPictureRect = IntRect( + aData.mPicX, aData.mPicY, aData.mPicSize.width, aData.mPicSize.height); + mYSize = aData.mYSize; + mCbCrSize = aData.mCbCrSize; + mColorSpace = aData.mYUVColorSpace; + + D3D11YCbCrRecycleAllocator* allocator = + aContainer->GetD3D11YCbCrRecycleAllocator(aAllocator); + if (!allocator) { + return false; + } + allocator->SetSizes(aData.mYSize, aData.mCbCrSize); + + mTextureClient = allocator->CreateOrRecycle(SurfaceFormat::A8, + mYSize, + BackendSelector::Content, + TextureFlags::DEFAULT); + if (!mTextureClient) { + return false; + } + + DXGIYCbCrTextureData *data = + static_cast(mTextureClient->GetInternalData()); + + ID3D11Texture2D* textureY = data->GetD3D11Texture(0); + ID3D11Texture2D* textureCb = data->GetD3D11Texture(1); + ID3D11Texture2D* textureCr = data->GetD3D11Texture(2); + + RefPtr mt; + HRESULT hr = allocator->GetDevice()->QueryInterface( + (ID3D10Multithread**)getter_AddRefs(mt)); + + if (FAILED(hr) || !mt) { + gfxCriticalError() << "Multithread safety interface not supported. " << hr; + return false; + } + + if (!mt->GetMultithreadProtected()) { + gfxCriticalError() << "Device used not marked as multithread-safe."; + return false; + } + + D3D11MTAutoEnter mtAutoEnter(mt.forget()); + + RefPtr ctx; + allocator->GetDevice()->GetImmediateContext(getter_AddRefs(ctx)); + + AutoLockD3D11Texture lockY(textureY); + AutoLockD3D11Texture lockCb(textureCb); + AutoLockD3D11Texture lockCr(textureCr); + + ctx->UpdateSubresource(textureY, + 0, + nullptr, + aData.mYChannel, + aData.mYStride, + aData.mYStride * aData.mYSize.height); + ctx->UpdateSubresource(textureCb, + 0, + nullptr, + aData.mCbChannel, + aData.mCbCrStride, + aData.mCbCrStride * aData.mCbCrSize.height); + ctx->UpdateSubresource(textureCr, + 0, + nullptr, + aData.mCrChannel, + aData.mCbCrStride, + aData.mCbCrStride * aData.mCbCrSize.height); + +return true; +} + +IntSize +D3D11YCbCrImage::GetSize() +{ + return mPictureRect.Size(); +} + +TextureClient* +D3D11YCbCrImage::GetTextureClient(KnowsCompositor* aForwarder) +{ + return mTextureClient; +} + +already_AddRefed +D3D11YCbCrImage::GetAsSourceSurface() +{ + if (!mTextureClient) { + gfxWarning() + << "GetAsSourceSurface() called on uninitialized D3D11YCbCrImage."; + return nullptr; + } + + gfx::IntSize size(mPictureRect.Size()); + gfx::SurfaceFormat format = + gfx::ImageFormatToSurfaceFormat(gfxVars::OffscreenFormat()); + HRESULT hr; + + PlanarYCbCrData data; + + DXGIYCbCrTextureData *dxgiData = + static_cast(mTextureClient->GetInternalData()); + + if (!dxgiData) { + gfxCriticalError() << "Failed to get texture client internal data."; + return nullptr; + } + + RefPtr texY = dxgiData->GetD3D11Texture(0); + RefPtr texCb = dxgiData->GetD3D11Texture(1); + RefPtr texCr = dxgiData->GetD3D11Texture(2); + RefPtr softTexY, softTexCb, softTexCr; + D3D11_TEXTURE2D_DESC desc; + + RefPtr dev; + texY->GetDevice(getter_AddRefs(dev)); + + RefPtr mt; + hr = dev->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt)); + + if (FAILED(hr) || !mt) { + gfxCriticalError() << "Multithread safety interface not supported."; + return nullptr; + } + + if (!mt->GetMultithreadProtected()) { + gfxCriticalError() << "Device used not marked as multithread-safe."; + return nullptr; + } + + D3D11MTAutoEnter mtAutoEnter(mt.forget()); + + texY->GetDesc(&desc); + desc.BindFlags = 0; + desc.MiscFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.Usage = D3D11_USAGE_STAGING; + + dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexY)); + + texCb->GetDesc(&desc); + desc.BindFlags = 0; + desc.MiscFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.Usage = D3D11_USAGE_STAGING; + + dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexCb)); + + texCr->GetDesc(&desc); + desc.BindFlags = 0; + desc.MiscFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.Usage = D3D11_USAGE_STAGING; + + dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexCr)); + + RefPtr ctx; + dev->GetImmediateContext(getter_AddRefs(ctx)); + + { + AutoLockD3D11Texture lockY(texY); + AutoLockD3D11Texture lockCb(texCb); + AutoLockD3D11Texture lockCr(texCr); + ctx->CopyResource(softTexY, texY); + ctx->CopyResource(softTexCb, texCb); + ctx->CopyResource(softTexCr, texCr); + } + + D3D11_MAPPED_SUBRESOURCE mapY, mapCb, mapCr; + RefPtr surface; + mapY.pData = mapCb.pData = mapCr.pData = nullptr; + + hr = ctx->Map(softTexY, 0, D3D11_MAP_READ, 0, &mapY); + if (FAILED(hr)) { + gfxCriticalError() << "Failed to map Y plane (" << hr << ")"; + return nullptr; + } + hr = ctx->Map(softTexCb, 0, D3D11_MAP_READ, 0, &mapCb); + if (FAILED(hr)) { + gfxCriticalError() << "Failed to map Y plane (" << hr << ")"; + return nullptr; + } + hr = ctx->Map(softTexCr, 0, D3D11_MAP_READ, 0, &mapCr); + if (FAILED(hr)) { + gfxCriticalError() << "Failed to map Y plane (" << hr << ")"; + return nullptr; + } + + MOZ_ASSERT(mapCb.RowPitch == mapCr.RowPitch); + + data.mPicX = mPictureRect.x; + data.mPicY = mPictureRect.y; + data.mPicSize = mPictureRect.Size(); + data.mStereoMode = StereoMode::MONO; + data.mYUVColorSpace = mColorSpace; + data.mYSkip = data.mCbSkip = data.mCrSkip = 0; + data.mYSize = mYSize; + data.mCbCrSize = mCbCrSize; + data.mYChannel = static_cast(mapY.pData); + data.mYStride = mapY.RowPitch; + data.mCbChannel = static_cast(mapCb.pData); + data.mCrChannel = static_cast(mapCr.pData); + data.mCbCrStride = mapCb.RowPitch; + + gfx::GetYCbCrToRGBDestFormatAndSize(data, format, size); + if (size.width > PlanarYCbCrImage::MAX_DIMENSION || + size.height > PlanarYCbCrImage::MAX_DIMENSION) { + gfxCriticalError() << "Illegal image dest width or height"; + return nullptr; + } + + surface = gfx::Factory::CreateDataSourceSurface(size, format); + if (!surface) { + gfxCriticalError() << "Failed to create DataSourceSurface for image: " + << size << " " << format; + return nullptr; + } + + DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE); + if (!mapping.IsMapped()) { + gfxCriticalError() << "Failed to map DataSourceSurface for D3D11YCbCrImage"; + return nullptr; + } + + gfx::ConvertYCbCrToRGB( + data, format, size, mapping.GetData(), mapping.GetStride()); + + ctx->Unmap(softTexY, 0); + ctx->Unmap(softTexCb, 0); + ctx->Unmap(softTexCr, 0); + + return surface.forget(); +} + +void +D3D11YCbCrRecycleAllocator::SetSizes(const gfx::IntSize& aYSize, + const gfx::IntSize& aCbCrSize) +{ + mYSize = Some(aYSize); + mCbCrSize = Some(aCbCrSize); +} + +already_AddRefed +D3D11YCbCrRecycleAllocator::Allocate(SurfaceFormat aFormat, + IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) +{ + MOZ_ASSERT(aFormat == SurfaceFormat::A8); + + gfx::IntSize YSize = mYSize.refOr(aSize); + gfx::IntSize CbCrSize = + mCbCrSize.refOr(gfx::IntSize(YSize.width, YSize.height)); + CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM, YSize.width, YSize.height, + 1, 1); + newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + + RefPtr mt; + HRESULT hr = mDevice->QueryInterface( + (ID3D10Multithread**)getter_AddRefs(mt)); + + if (FAILED(hr) || !mt) { + gfxCriticalError() << "Multithread safety interface not supported. " << hr; + return nullptr; + } + + if (!mt->GetMultithreadProtected()) { + gfxCriticalError() << "Device used not marked as multithread-safe."; + return nullptr; + } + + D3D11MTAutoEnter mtAutoEnter(mt.forget()); + + RefPtr textureY; + hr = mDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureY)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + newDesc.Width = CbCrSize.width; + newDesc.Height = CbCrSize.height; + + RefPtr textureCb; + hr = mDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureCb)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + RefPtr textureCr; + hr = mDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureCr)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + return TextureClient::CreateWithData( + DXGIYCbCrTextureData::Create( + textureY, + textureCb, + textureCr, + aSize, + YSize, + CbCrSize), + TextureFlags::DEFAULT, + mSurfaceAllocator->GetTextureForwarder()); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/D3D11YCbCrImage.h b/gfx/layers/D3D11YCbCrImage.h new file mode 100644 index 0000000000..060b647501 --- /dev/null +++ b/gfx/layers/D3D11YCbCrImage.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_D3D11_YCBCR_IMAGE_H +#define GFX_D3D11_YCBCR_IMAGE_H + +#include "d3d11.h" +#include "mozilla/layers/TextureClientRecycleAllocator.h" +#include "mozilla/Maybe.h" +#include "ImageContainer.h" + +namespace mozilla { +namespace layers { + +class ImageContainer; +class DXGIYCbCrTextureClient; + +class D3D11YCbCrRecycleAllocator : public TextureClientRecycleAllocator +{ +public: + explicit D3D11YCbCrRecycleAllocator(KnowsCompositor* aAllocator, + ID3D11Device* aDevice) + : TextureClientRecycleAllocator(aAllocator) + , mDevice(aDevice) + { + } + + ID3D11Device* GetDevice() { return mDevice; } + KnowsCompositor* GetAllocator() { return mSurfaceAllocator; } + void SetSizes(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize); + +protected: + already_AddRefed + Allocate(gfx::SurfaceFormat aFormat, + gfx::IntSize aSize, + BackendSelector aSelector, + TextureFlags aTextureFlags, + TextureAllocationFlags aAllocFlags) override; + + RefPtr mDevice; + Maybe mYSize; + Maybe mCbCrSize; +}; + +class D3D11YCbCrImage : public Image +{ +public: + D3D11YCbCrImage(); + virtual ~D3D11YCbCrImage(); + + // Copies the surface into a sharable texture's surface, and initializes + // the image. + bool SetData(KnowsCompositor* aAllocator, + ImageContainer* aContainer, + const PlanarYCbCrData& aData); + + gfx::IntSize GetSize() override; + + already_AddRefed GetAsSourceSurface() override; + + TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override; + + gfx::IntRect GetPictureRect() override { return mPictureRect; } + +private: + gfx::IntSize mYSize; + gfx::IntSize mCbCrSize; + gfx::IntRect mPictureRect; + YUVColorSpace mColorSpace; + RefPtr mTextureClient; +}; + +} // namepace layers +} // namespace mozilla + +#endif // GFX_D3D11_YCBCR_IMAGE_H diff --git a/gfx/layers/IMFYCbCrImage.cpp b/gfx/layers/IMFYCbCrImage.cpp index 6740fdbe07..25e2814174 100644 --- a/gfx/layers/IMFYCbCrImage.cpp +++ b/gfx/layers/IMFYCbCrImage.cpp @@ -34,38 +34,6 @@ IMFYCbCrImage::~IMFYCbCrImage() } } -struct AutoLockTexture -{ - AutoLockTexture(ID3D11Texture2D* aTexture) - { - aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex)); - if (!mMutex) { - return; - } - HRESULT hr = mMutex->AcquireSync(0, 10000); - if (hr == WAIT_TIMEOUT) { - MOZ_CRASH("GFX: IMFYCbCrImage timeout"); - } - - if (FAILED(hr)) { - NS_WARNING("Failed to lock the texture"); - } - } - - ~AutoLockTexture() - { - if (!mMutex) { - return; - } - HRESULT hr = mMutex->ReleaseSync(0); - if (FAILED(hr)) { - NS_WARNING("Failed to unlock the texture"); - } - } - - RefPtr mMutex; -}; - static already_AddRefed InitTextures(IDirect3DDevice9* aDevice, const IntSize &aSize, @@ -226,6 +194,112 @@ IMFYCbCrImage::GetD3D9TextureClient(KnowsCompositor* aForwarder) return mTextureClient; } +DXGIYCbCrTextureData* +IMFYCbCrImage::GetD3D11TextureData(Data aData, gfx::IntSize aSize) +{ + HRESULT hr; + RefPtr mt; + + RefPtr device = gfx::DeviceManagerDx::Get()->GetContentDevice(); + + if (!device) { + device = gfx::DeviceManagerDx::Get()->GetCompositorDevice(); + } + + if (!gfx::DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) { + return nullptr; + } + + hr = device->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt)); + + if (FAILED(hr)) { + return nullptr; + } + + if (!mt->GetMultithreadProtected()) { + return nullptr; + } + + if (aData.mYStride < 0 || aData.mCbCrStride < 0) { + // D3D11 only supports unsigned stride values. + return nullptr; + } + + CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM, + aData.mYSize.width, aData.mYSize.height, 1, 1); + + if (device == gfx::DeviceManagerDx::Get()->GetCompositorDevice()) { + newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; + } else { + newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + } + + RefPtr textureY; + hr = device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureY)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + newDesc.Width = aData.mCbCrSize.width; + newDesc.Height = aData.mCbCrSize.height; + + RefPtr textureCb; + hr = device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureCb)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + RefPtr textureCr; + hr = device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(textureCr)); + NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); + + // The documentation here seems to suggest using the immediate mode context + // on more than one thread is not allowed: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ff476891(v=vs.85).aspx + // The Debug Layer seems to imply it is though. When the ID3D10Multithread + // layer is on. The Enter/Leave of the critical section shouldn't even be + // required but were added for extra security. + + { + AutoLockD3D11Texture lockY(textureY); + AutoLockD3D11Texture lockCr(textureCr); + AutoLockD3D11Texture lockCb(textureCb); + + D3D11MTAutoEnter mtAutoEnter(mt.forget()); + + RefPtr ctx; + device->GetImmediateContext((ID3D11DeviceContext**)getter_AddRefs(ctx)); + + D3D11_BOX box; + box.front = box.top = box.left = 0; + box.back = 1; + box.right = aData.mYSize.width; + box.bottom = aData.mYSize.height; + ctx->UpdateSubresource(textureY, 0, &box, aData.mYChannel, aData.mYStride, 0); + + box.right = aData.mCbCrSize.width; + box.bottom = aData.mCbCrSize.height; + ctx->UpdateSubresource(textureCb, 0, &box, aData.mCbChannel, aData.mCbCrStride, 0); + ctx->UpdateSubresource(textureCr, 0, &box, aData.mCrChannel, aData.mCbCrStride, 0); + } + + return DXGIYCbCrTextureData::Create(textureY, textureCb, textureCr, + aSize, aData.mYSize, aData.mCbCrSize); +} + +TextureClient* +IMFYCbCrImage::GetD3D11TextureClient(KnowsCompositor* aForwarder) +{ + DXGIYCbCrTextureData* textureData = GetD3D11TextureData(mData, GetSize()); + + if (textureData == nullptr) { + return nullptr; + } + + mTextureClient = TextureClient::CreateWithData( + textureData, TextureFlags::DEFAULT, + aForwarder->GetTextureForwarder() + ); + + return mTextureClient; +} + TextureClient* IMFYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder) { @@ -233,11 +307,9 @@ IMFYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder) return mTextureClient; } - RefPtr device = - gfx::DeviceManagerDx::Get()->GetContentDevice(); + RefPtr device = gfx::DeviceManagerDx::Get()->GetContentDevice(); if (!device) { - device = - gfx::DeviceManagerDx::Get()->GetCompositorDevice(); + device = gfx::DeviceManagerDx::Get()->GetCompositorDevice(); } LayersBackend backend = aForwarder->GetCompositorBackendType(); @@ -248,60 +320,7 @@ IMFYCbCrImage::GetTextureClient(KnowsCompositor* aForwarder) } return nullptr; } - - if (!gfx::DeviceManagerDx::Get()->CanInitializeKeyedMutexTextures()) { - return nullptr; - } - - if (mData.mYStride < 0 || mData.mCbCrStride < 0) { - // D3D11 only supports unsigned stride values. - return nullptr; - } - - CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_R8_UNORM, - mData.mYSize.width, mData.mYSize.height, 1, 1); - - if (device == gfx::DeviceManagerDx::Get()->GetCompositorDevice()) { - newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; - } else { - newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; - } - - RefPtr textureY; - D3D11_SUBRESOURCE_DATA yData = { mData.mYChannel, (UINT)mData.mYStride, 0 }; - HRESULT hr = device->CreateTexture2D(&newDesc, &yData, getter_AddRefs(textureY)); - NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); - - newDesc.Width = mData.mCbCrSize.width; - newDesc.Height = mData.mCbCrSize.height; - - RefPtr textureCb; - D3D11_SUBRESOURCE_DATA cbData = { mData.mCbChannel, (UINT)mData.mCbCrStride, 0 }; - hr = device->CreateTexture2D(&newDesc, &cbData, getter_AddRefs(textureCb)); - NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); - - RefPtr textureCr; - D3D11_SUBRESOURCE_DATA crData = { mData.mCrChannel, (UINT)mData.mCbCrStride, 0 }; - hr = device->CreateTexture2D(&newDesc, &crData, getter_AddRefs(textureCr)); - NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr); - - // Even though the textures we created are meant to be protected by a keyed mutex, - // it appears that D3D doesn't include the initial memory upload within this - // synchronization. Add an empty lock/unlock pair since that appears to - // be sufficient to make sure we synchronize. - { - AutoLockTexture lockCr(textureCr); - } - - mTextureClient = TextureClient::CreateWithData( - DXGIYCbCrTextureData::Create(TextureFlags::DEFAULT, - textureY, textureCb, textureCr, - GetSize(), mData.mYSize, mData.mCbCrSize), - TextureFlags::DEFAULT, - aForwarder->GetTextureForwarder() - ); - - return mTextureClient; + return GetD3D11TextureClient(aForwarder); } } // namespace layers diff --git a/gfx/layers/IMFYCbCrImage.h b/gfx/layers/IMFYCbCrImage.h index 9a42987279..6b4e90cf1f 100644 --- a/gfx/layers/IMFYCbCrImage.h +++ b/gfx/layers/IMFYCbCrImage.h @@ -6,6 +6,7 @@ #ifndef GFX_IMFYCBCRIMAGE_H #define GFX_IMFYCBCRIMAGE_H +#include "mozilla/layers/TextureD3D11.h" #include "mozilla/RefPtr.h" #include "ImageContainer.h" #include "mfidl.h" @@ -22,10 +23,15 @@ public: virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override; + static DXGIYCbCrTextureData* GetD3D11TextureData(Data aData, + gfx::IntSize aSize); + protected: TextureClient* GetD3D9TextureClient(KnowsCompositor* aForwarder); + TextureClient* GetD3D11TextureClient(KnowsCompositor* aForwarder); + ~IMFYCbCrImage(); RefPtr mBuffer; diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index 5e4019e861..c982b5cb8f 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -35,6 +35,8 @@ #ifdef XP_WIN #include "gfxWindowsPlatform.h" #include +#include "mozilla/gfx/DeviceManagerDx.h" +#include "mozilla/layers/D3D11YCbCrImage.h" #endif namespace mozilla { @@ -395,6 +397,40 @@ ImageContainer::NotifyCompositeInternal(const ImageCompositeNotification& aNotif } } +#ifdef XP_WIN +D3D11YCbCrRecycleAllocator* +ImageContainer::GetD3D11YCbCrRecycleAllocator(KnowsCompositor* aAllocator) +{ + if (mD3D11YCbCrRecycleAllocator && + aAllocator == mD3D11YCbCrRecycleAllocator->GetAllocator()) { + return mD3D11YCbCrRecycleAllocator; + } + + RefPtr device = gfx::DeviceManagerDx::Get()->GetContentDevice(); + if (!device) { + device = gfx::DeviceManagerDx::Get()->GetCompositorDevice(); + } + + LayersBackend backend = aAllocator->GetCompositorBackendType(); + if (!device || backend != LayersBackend::LAYERS_D3D11) { + return nullptr; + } + + RefPtr multi; + HRESULT hr = + device->QueryInterface((ID3D10Multithread**)getter_AddRefs(multi)); + if (FAILED(hr) || !multi) { + gfxWarning() << "Multithread safety interface not supported. " << hr; + return nullptr; + } + multi->SetMultithreadProtected(TRUE); + + mD3D11YCbCrRecycleAllocator = + new D3D11YCbCrRecycleAllocator(aAllocator, device); + return mD3D11YCbCrRecycleAllocator; +} +#endif + PlanarYCbCrImage::PlanarYCbCrImage() : Image(nullptr, ImageFormat::PLANAR_YCBCR) , mOffscreenFormat(SurfaceFormat::UNKNOWN) diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index f3e88665fb..79f27bed7a 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -153,6 +153,9 @@ class PlanarYCbCrImage; class TextureClient; class KnowsCompositor; class NVImage; +#ifdef XP_WIN +class D3D11YCbCrRecycleAllocator; +#endif struct ImageBackendData { @@ -173,14 +176,14 @@ class MacIOSurfaceImage; /** * A class representing a buffer of pixel data. The data can be in one * of various formats including YCbCr. - * + * * Create an image using an ImageContainer. Fill the image with data, and * then call ImageContainer::SetImage to display it. An image must not be * modified after calling SetImage. Image implementations do not need to * perform locking; when filling an Image, the Image client is responsible * for ensuring only one thread accesses the Image at a time, and after * SetImage the image is immutable. - * + * * When resampling an Image, only pixels within the buffer should be * sampled. For example, cairo images should be sampled in EXTEND_PAD mode. */ @@ -254,7 +257,7 @@ protected: /** * A RecycleBin is owned by an ImageContainer. We store buffers in it that we - * want to recycle from one image to the next.It's a separate object from + * want to recycle from one image to the next.It's a separate object from * ImageContainer because images need to store a strong ref to their RecycleBin * and we must avoid creating a reference loop between an ImageContainer and * its active image. @@ -319,7 +322,7 @@ protected: const gfx::IntSize& aScaleHint, BufferRecycleBin *aRecycleBin); }; - + /** * A class that manages Images for an ImageLayer. The only reason * we need a separate class here is that ImageLayers aren't threadsafe @@ -399,7 +402,7 @@ public: * mProducerID is a unique ID for the stream of images. A change in the * mProducerID means changing to a new mFrameID namespace. All frames in * aImages must have the same mProducerID. - * + * * The Image data must not be modified after this method is called! * Note that this must not be called if ENABLE_ASYNC has not been set. * @@ -435,11 +438,11 @@ public: * Set an Image as the current image to display. The Image must have * been created by this ImageContainer. * Must be called on the main thread, within a layers transaction. - * + * * This method takes mReentrantMonitor * when accessing thread-shared state. * aImage can be null. While it's null, nothing will be painted. - * + * * The Image data must not be modified after this method is called! * Note that this must not be called if ENABLE_ASYNC been set. * @@ -521,6 +524,11 @@ public: return mImageFactory; } +#ifdef XP_WIN + D3D11YCbCrRecycleAllocator* GetD3D11YCbCrRecycleAllocator( + KnowsCompositor* aAllocator); +#endif + /** * Returns the delay between the last composited image's presentation * timestamp and when it was first composited. It's possible for the delay @@ -587,6 +595,10 @@ private: // image", and any other state which is shared between threads. ReentrantMonitor mReentrantMonitor; +#ifdef XP_WIN + RefPtr mD3D11YCbCrRecycleAllocator; +#endif + nsTArray mCurrentImages; // Updates every time mActiveImage changes @@ -703,7 +715,7 @@ struct PlanarYCbCrData { * * The color format is detected based on the height/width ratios * defined above. - * + * * The Image that is rendered is the picture region defined by * mPicX, mPicY and mPicSize. The size of the rendered image is * mPicSize, not mYSize or mCbCrSize. diff --git a/gfx/layers/ImageTypes.h b/gfx/layers/ImageTypes.h index 5f6981fef7..078c94989c 100644 --- a/gfx/layers/ImageTypes.h +++ b/gfx/layers/ImageTypes.h @@ -99,6 +99,11 @@ enum class ImageFormat { */ TEXTURE_WRAPPER, + /** + * A D3D11 backed YUV image. + */ + D3D11_YCBCR_IMAGE, + /** * An opaque handle that refers to an Image stored in the GPU * process. diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h index ba521f8deb..9249394a5c 100644 --- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -618,7 +618,7 @@ public: /** * Set last transaction id of CompositableForwarder. - * + * * Called when TextureClient has TextureFlags::RECYCLE flag. * When CompositableForwarder forwards the TextureClient with * TextureFlags::RECYCLE, it holds TextureClient's ref until host side @@ -644,7 +644,7 @@ public: private: static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure); - + // Internal helpers for creating texture clients using the actual forwarder instead // of KnowsCompositor. TextureClientPool uses these to let it cache texture clients // per-process instead of per ShadowLayerForwarder, but everyone else should @@ -659,7 +659,7 @@ private: BackendSelector aSelector, TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT); - + static already_AddRefed CreateForRawBufferAccess(LayersIPCChannel* aAllocator, gfx::SurfaceFormat aFormat, @@ -735,6 +735,8 @@ protected: friend class TextureChild; friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*); friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&); + friend already_AddRefed CreateTextureHostWithBackend( + TextureClient*, LayersBackend&); #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL public: diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 628ca12882..57c08e8037 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -516,10 +516,9 @@ D3D11TextureData::GetDXGIResource(IDXGIResource** aOutResource) } DXGIYCbCrTextureData* -DXGIYCbCrTextureData::Create(TextureFlags aFlags, - IUnknown* aTextureY, - IUnknown* aTextureCb, - IUnknown* aTextureCr, +DXGIYCbCrTextureData::Create(IDirect3DTexture9* aTextureY, + IDirect3DTexture9* aTextureCb, + IDirect3DTexture9* aTextureCr, HANDLE aHandleY, HANDLE aHandleCb, HANDLE aHandleCr, @@ -536,9 +535,9 @@ DXGIYCbCrTextureData::Create(TextureFlags aFlags, texture->mHandles[0] = aHandleY; texture->mHandles[1] = aHandleCb; texture->mHandles[2] = aHandleCr; - texture->mHoldRefs[0] = aTextureY; - texture->mHoldRefs[1] = aTextureCb; - texture->mHoldRefs[2] = aTextureCr; + texture->mD3D9Textures[0] = aTextureY; + texture->mD3D9Textures[1] = aTextureCb; + texture->mD3D9Textures[2] = aTextureCr; texture->mSize = aSize; texture->mSizeY = aSizeY; texture->mSizeCbCr = aSizeCbCr; @@ -547,8 +546,7 @@ DXGIYCbCrTextureData::Create(TextureFlags aFlags, } DXGIYCbCrTextureData* -DXGIYCbCrTextureData::Create(TextureFlags aFlags, - ID3D11Texture2D* aTextureY, +DXGIYCbCrTextureData::Create(ID3D11Texture2D* aTextureY, ID3D11Texture2D* aTextureCb, ID3D11Texture2D* aTextureCr, const gfx::IntSize& aSize, @@ -591,10 +589,18 @@ DXGIYCbCrTextureData::Create(TextureFlags aFlags, return nullptr; } - return DXGIYCbCrTextureData::Create(aFlags, - aTextureY, aTextureCb, aTextureCr, - handleY, handleCb, handleCr, - aSize, aSizeY, aSizeCbCr); + DXGIYCbCrTextureData* texture = new DXGIYCbCrTextureData(); + texture->mHandles[0] = handleY; + texture->mHandles[1] = handleCb; + texture->mHandles[2] = handleCr; + texture->mD3D11Textures[0] = aTextureY; + texture->mD3D11Textures[1] = aTextureCb; + texture->mD3D11Textures[2] = aTextureCr; + texture->mSize = aSize; + texture->mSizeY = aSizeY; + texture->mSizeCbCr = aSizeCbCr; + + return texture; } void @@ -620,9 +626,12 @@ DXGIYCbCrTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) void DXGIYCbCrTextureData::Deallocate(LayersIPCChannel*) { - mHoldRefs[0] = nullptr; - mHoldRefs[1] = nullptr; - mHoldRefs[2] = nullptr; + mD3D9Textures[0] = nullptr; + mD3D9Textures[1] = nullptr; + mD3D9Textures[2] = nullptr; + mD3D11Textures[0] = nullptr; + mD3D11Textures[1] = nullptr; + mD3D11Textures[2] = nullptr; } already_AddRefed @@ -1282,5 +1291,32 @@ SyncObjectD3D11::FinalizeFrame() } } +AutoLockD3D11Texture::AutoLockD3D11Texture(ID3D11Texture2D* aTexture) +{ + aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex)); + if (!mMutex) { + return; + } + HRESULT hr = mMutex->AcquireSync(0, 10000); + if (hr == WAIT_TIMEOUT) { + MOZ_CRASH("GFX: IMFYCbCrImage timeout"); + } + + if (FAILED(hr)) { + NS_WARNING("Failed to lock the texture"); + } +} + +AutoLockD3D11Texture::~AutoLockD3D11Texture() +{ + if (!mMutex) { + return; + } + HRESULT hr = mMutex->ReleaseSync(0); + if (FAILED(hr)) { + NS_WARNING("Failed to unlock the texture"); + } +} + } } diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h index 7e24410f3d..86db51df13 100644 --- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -127,10 +127,9 @@ class DXGIYCbCrTextureData : public TextureData { public: static DXGIYCbCrTextureData* - Create(TextureFlags aFlags, - IUnknown* aTextureY, - IUnknown* aTextureCb, - IUnknown* aTextureCr, + Create(IDirect3DTexture9* aTextureY, + IDirect3DTexture9* aTextureCb, + IDirect3DTexture9* aTextureCr, HANDLE aHandleY, HANDLE aHandleCb, HANDLE aHandleCr, @@ -139,8 +138,7 @@ public: const gfx::IntSize& aSizeCbCr); static DXGIYCbCrTextureData* - Create(TextureFlags aFlags, - ID3D11Texture2D* aTextureCb, + Create(ID3D11Texture2D* aTextureCb, ID3D11Texture2D* aTextureY, ID3D11Texture2D* aTextureCr, const gfx::IntSize& aSize, @@ -166,8 +164,11 @@ public: return TextureFlags::DEALLOCATE_MAIN_THREAD; } + ID3D11Texture2D* GetD3D11Texture(size_t index) { return mD3D11Textures[index]; } + protected: - RefPtr mHoldRefs[3]; + RefPtr mD3D11Textures[3]; + RefPtr mD3D9Textures[3]; HANDLE mHandles[3]; gfx::IntSize mSize; gfx::IntSize mSizeY; @@ -450,6 +451,30 @@ inline uint32_t GetMaxTextureSizeForFeatureLevel(D3D_FEATURE_LEVEL aFeatureLevel return maxTextureSize; } +class AutoLockD3D11Texture +{ +public: + explicit AutoLockD3D11Texture(ID3D11Texture2D* aTexture); + ~AutoLockD3D11Texture(); + +private: + RefPtr mMutex; +}; + +class D3D11MTAutoEnter +{ +public: + explicit D3D11MTAutoEnter(already_AddRefed aMT) + : mMT(aMT) + { + mMT->Enter(); + } + ~D3D11MTAutoEnter() { mMT->Leave(); } + +private: + RefPtr mMT; +}; + } } diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index 318be2a3f7..1f1caa058b 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -48,6 +48,7 @@ EXPORTS += [ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': SOURCES += [ 'D3D11ShareHandleImage.cpp', + 'D3D11YCbCrImage.cpp', ] UNIFIED_SOURCES += [ 'D3D9SurfaceImage.cpp', @@ -153,6 +154,7 @@ EXPORTS.mozilla.layers += [ 'Compositor.h', 'CompositorTypes.h', 'D3D11ShareHandleImage.h', + 'D3D11YCbCrImage.h', 'D3D9SurfaceImage.h', 'Effects.h', 'ImageDataSerializer.h', diff --git a/gfx/thebes/DeviceManagerDx.cpp b/gfx/thebes/DeviceManagerDx.cpp index 5f505f88ba..a55d1a72a0 100644 --- a/gfx/thebes/DeviceManagerDx.cpp +++ b/gfx/thebes/DeviceManagerDx.cpp @@ -781,7 +781,7 @@ DeviceManagerDx::CanInitializeKeyedMutexTextures() } // Disable this on all Intel devices because of crashes. // See bug 1292923. - return mDeviceStatus->adapter().VendorId != 0x8086; + return (mDeviceStatus->adapter().VendorId != 0x8086 || gfxPrefs::Direct3D11AllowIntelMutex()); } bool diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index fd5946cb2a..382cc0ee20 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -376,6 +376,7 @@ private: DECL_GFX_PREF(Once, "gfx.direct2d.disabled", Direct2DDisabled, bool, false); DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled", Direct2DForceEnabled, bool, false); DECL_GFX_PREF(Live, "gfx.direct3d11.reuse-decoder-device", Direct3D11ReuseDecoderDevice, int32_t, -1); + DECL_GFX_PREF(Live, "gfx.direct3d11.allow-intel-mutex", Direct3D11AllowIntelMutex, bool, true); DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false); DECL_GFX_PREF(Once, "gfx.e10s.hide-plugins-for-scroll", HidePluginsForScroll, bool, true); DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels", GrallocFenceWithReadPixels, bool, false); @@ -601,7 +602,7 @@ private: DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false); DECL_GFX_PREF(Live, "webgl.max-warnings-per-context", WebGLMaxWarningsPerContext, uint32_t, 32); DECL_GFX_PREF(Live, "webgl.max-size-per-texture-mb", WebGLMaxSizePerTextureMB, uint32_t, 1024); - DECL_GFX_PREF(Live, "webgl.max-vert-ids-per-draw", WebglMaxVertIDsPerDraw, uint32_t, 30*1000*1000); + DECL_GFX_PREF(Live, "webgl.max-vert-ids-per-draw", WebglMaxVertIDsPerDraw, uint32_t, 30*1000*1000); DECL_GFX_PREF(Live, "webgl.min_capability_mode", WebGLMinCapabilityMode, bool, false); DECL_GFX_PREF(Live, "webgl.msaa-force", WebGLForceMSAA, bool, false); DECL_GFX_PREF(Live, "webgl.prefer-16bpp", WebGLPrefer16bpp, bool, false); diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index dd03017fc0..16a5808ab2 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -1607,7 +1607,10 @@ js::StartDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, Hand JS::ModuleDynamicImportHook importHook = cx->runtime()->moduleDynamicImportHook; MOZ_ASSERT(importHook); + cx->runtime()->addRefScriptPrivate(referencingPrivate); if (!importHook(cx, referencingPrivate, specifier, promise)) { + cx->runtime()->releaseScriptPrivate(referencingPrivate); + if (!RejectPromiseWithPendingError(cx, promise)) return nullptr; return promise; @@ -1622,6 +1625,9 @@ js::FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate, Han { Handle promise = promiseArg.as(); + auto releasePrivate = mozilla::MakeScopeExit( + [&] { cx->runtime()->releaseScriptPrivate(referencingPrivate); }); + if (cx->isExceptionPending()) { return RejectPromiseWithPendingError(cx, promise); } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 9067ca3d3d..1be4e39113 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4729,7 +4729,8 @@ JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options, JS_PUBLIC_API(void) JS::SetModulePrivate(JSObject* module, const JS::Value& value) { - module->as().scriptSourceObject()->setPrivate(value); + JSRuntime* rt = module->compartment()->runtimeFromMainThread(); + module->as().scriptSourceObject()->setPrivate(rt, value); } JS_PUBLIC_API(JS::Value) @@ -4741,7 +4742,8 @@ JS::GetModulePrivate(JSObject* module) JS_PUBLIC_API(void) JS::SetScriptPrivate(JSScript* script, const JS::Value& value) { - script->scriptSourceUnwrap().setPrivate(value); + JSRuntime* rt = script->compartment()->runtimeFromMainThread(); + script->scriptSourceUnwrap().setPrivate(rt, value); } JS_PUBLIC_API(JS::Value) @@ -4764,18 +4766,13 @@ JS::GetScriptedCallerPrivate(JSContext* cx) return FindScriptOrModulePrivateForScript(iter.script()); } -JS_PUBLIC_API(JS::ScriptPrivateFinalizeHook) -JS::GetScriptPrivateFinalizeHook(JSContext* cx) -{ - AssertHeapIsIdle(cx); - return cx->runtime()->scriptPrivateFinalizeHook; -} - JS_PUBLIC_API(void) -JS::SetScriptPrivateFinalizeHook(JSContext* cx, JS::ScriptPrivateFinalizeHook func) +JS::SetScriptPrivateReferenceHooks(JSContext* cx, JS::ScriptPrivateReferenceHook addRefHook, + JS::ScriptPrivateReferenceHook releaseHook) { AssertHeapIsIdle(cx); - cx->runtime()->scriptPrivateFinalizeHook = func; + cx->runtime()->scriptPrivateAddRefHook = addRefHook; + cx->runtime()->scriptPrivateReleaseHook = releaseHook; } JS_PUBLIC_API(bool) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 951b612502..444b9e2ed2 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4432,24 +4432,18 @@ extern JS_PUBLIC_API(JS::Value) GetScriptedCallerPrivate(JSContext* cx); /** - * A hook that's called whenever a script or module which has a private value - * set with SetScriptPrivate() or SetModulePrivate() is finalized. This can be - * used to clean up the private state. The private value is passed as an - * argument. + * Hooks called when references to a script private value are created or + * destroyed. This allows use of a reference counted object as the + * script private. */ -using ScriptPrivateFinalizeHook = void (*)(JSFreeOp*, const JS::Value&); - -/** - * Get the script private finalize hook for the runtime. - */ -extern JS_PUBLIC_API(ScriptPrivateFinalizeHook) -GetScriptPrivateFinalizeHook(JSContext* cx); +using ScriptPrivateReferenceHook = void (*)(const JS::Value&); /** * Set the script private finalize hook for the runtime to the given function. */ extern JS_PUBLIC_API(void) -SetScriptPrivateFinalizeHook(JSContext* cx, ScriptPrivateFinalizeHook func); +SetScriptPrivateReferenceHooks(JSContext* cx, ScriptPrivateReferenceHook addRefHook, + ScriptPrivateReferenceHook releaseHook); /* * Perform the ModuleInstantiate operation on the given source text module diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 5cf0d19195..a438f051c9 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1419,15 +1419,8 @@ ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj) sso->source()->decref(); sso->setReservedSlot(SOURCE_SLOT, PrivateValue(nullptr)); - Value value = sso->canonicalPrivate(); - if (!value.isUndefined()) { - // The embedding may need to dispose of its private data. - JS::AutoSuppressGCAnalysis suppressGC; - if (JS::ScriptPrivateFinalizeHook hook = - fop->runtime()->scriptPrivateFinalizeHook) { - hook(fop, value); - } - } + // Clear the private value, calling the release hook if necessary. + sso->setPrivate(fop->runtime(), UndefinedValue()); } static const ClassOps ScriptSourceObjectClassOps = { @@ -1532,6 +1525,25 @@ ScriptSourceObject::initFromOptions(JSContext* cx, HandleScriptSource source, return true; } +void ScriptSourceObject::setPrivate(JSRuntime* rt, const Value& value) +{ + // Update the private value, calling addRef/release hooks if necessary + // to allow the embedding to maintain a reference count for the + // private data. + Value prevValue = getReservedSlot(PRIVATE_SLOT); + if (!prevValue.isUndefined()) { + if (auto releaseHook = rt->scriptPrivateReleaseHook) { + releaseHook(prevValue); + } + } + setReservedSlot(PRIVATE_SLOT, value); + if (!value.isUndefined()) { + if (auto addRefHook = rt->scriptPrivateAddRefHook) { + addRefHook(value); + } + } +} + /* static */ bool JSScript::loadSource(JSContext* cx, ScriptSource* ss, bool* worked) { diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 78afa2eec2..68e9884321 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -675,9 +675,7 @@ class ScriptSourceObject : public NativeObject return static_cast(untyped); } - void setPrivate(const Value& value) { - setReservedSlot(PRIVATE_SLOT, value); - } + void setPrivate(JSRuntime* rt, const Value& value); Value getPrivate() const { return getReservedSlot(PRIVATE_SLOT); diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index c5deff940c..a80359f282 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -252,7 +252,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) moduleResolveHook(), moduleMetadataHook(), moduleDynamicImportHook(), - scriptPrivateFinalizeHook() + scriptPrivateAddRefHook(), + scriptPrivateReleaseHook() { setGCStoreBufferPtr(&gc.storeBuffer); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 13523844c1..6f781d101c 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1316,8 +1316,21 @@ struct JSRuntime : public JS::shadow::Runtime, // HostImportModuleDynamically. JS::ModuleDynamicImportHook moduleDynamicImportHook; - // A hook called on script finalization. - JS::ScriptPrivateFinalizeHook scriptPrivateFinalizeHook; + // Hooks called when script private references are created and destroyed. + JS::ScriptPrivateReferenceHook scriptPrivateAddRefHook; + JS::ScriptPrivateReferenceHook scriptPrivateReleaseHook; + + void addRefScriptPrivate(const JS::Value& value) { + if (!value.isUndefined() && scriptPrivateAddRefHook) { + scriptPrivateAddRefHook(value); + } + } + + void releaseScriptPrivate(const JS::Value& value) { + if (!value.isUndefined() && scriptPrivateReleaseHook) { + scriptPrivateReleaseHook(value); + } + } }; namespace js {