/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using layers::D3D11RecycleAllocator; using layers::D3D11ShareHandleImage; using layers::Image; using layers::ImageContainer; usingnamespace layers; usingnamespace gfx;
// The D3D format is the first DWORD of the subtype GUID.
GUID subtype = GUID_NULL;
HRESULT hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
pDesc->Format = (D3DFORMAT)subtype.Data1;
staticconstchar* DecoderGUIDToStr(const GUID& aGuid) { if (aGuid == DXVA2_ModeH264_VLD_NoFGT) { return"H264";
} if (aGuid == DXVA2_Intel_ClearVideo_ModeH264_VLD_NoFGT) { return"Intel H264";
} if (aGuid == DXVA2_ModeVP8_VLD) { return"VP8";
} if (aGuid == DXVA2_ModeVP9_VLD_Profile0) { return"VP9 Profile0";
} if (aGuid == DXVA2_ModeVP9_VLD_10bit_Profile2) { return"VP9 10bits Profile2";
} if (aGuid == DXVA2_ModeAV1_VLD_Profile0) { return"AV1 Profile0";
} if (aGuid == DXVA2_ModeAV1_VLD_Profile1) { return"AV1 Profile1";
} if (aGuid == DXVA2_ModeAV1_VLD_Profile2) { return"AV1 Profile2";
} if (aGuid == DXVA2_ModeAV1_VLD_12bit_Profile2) { return"AV1 12bits Profile2";
} if (aGuid == DXVA2_ModeAV1_VLD_12bit_Profile2_420) { return"AV1 12bits Profile2 420";
} if (aGuid == DXVA2_ModeHEVC_VLD_MAIN) { return"HEVC main";
} if (aGuid == DXVA2_ModeHEVC_VLD_MAIN10) { return"HEVC main10";
} return"none";
}
// Count of the number of DXVAManager's we've created. This is also the // number of videos we're decoding with DXVA. Use on main thread only. static Atomic<uint32_t> sDXVAVideosCount(0);
// This class's functions are not thread-safe, please use them carefully. // TODO : make this class better in bug1932998. class D3D11DXVA2Manager : public DXVA2Manager { public:
D3D11DXVA2Manager(); virtual ~D3D11DXVA2Manager();
// Copies a region (aRegion) of the video frame stored in aVideoSample // into an image which is returned by aOutImage.
HRESULT CopyToImage(IMFSample* aVideoSample, const gfx::IntRect& aRegion,
Image** aOutImage) override;
HRESULT CopyToImage(ID3D11Texture2D* aVideoSample, UINT aSurfaceIndex, const gfx::IntRect& aRegion,
layers::Image** aOutImage) override;
// This is used for check whether hw decoding is possible before using MFT for // decoding. bool CanCreateDecoder(const D3D11_VIDEO_DECODER_DESC& aDesc) const;
if (aInfo.mExtraData && !aInfo.mExtraData->IsEmpty()) {
VPXDecoder::VPXStreamInfo vp9Info;
VPXDecoder::ReadVPCCBox(vp9Info, aInfo.mExtraData);
profile = vp9Info.mProfile;
} else { // If no vpcC is present, we can't know the profile, which limits the // subsampling mode, but 4:2:0 is most supported so default to profiles 0 // and 2: // Profile 0 = 8bit, 4:2:0 // Profile 2 = 10/12bit, 4:2:0
profile = aInfo.mColorDepth == ColorDepth::COLOR_8 ? 0 : 2;
}
if (layers::ImageBridgeChild::GetSingleton() || !aKnowsCompositor) { // There's no proper KnowsCompositor for ImageBridge currently (and it // implements the interface), so just use that if it's available.
mTextureClientAllocator = new D3D11RecycleAllocator(
layers::ImageBridgeChild::GetSingleton().get(), mDevice,
gfx::SurfaceFormat::NV12);
if (ImageBridgeChild::GetSingleton() &&
StaticPrefs::media_wmf_use_sync_texture_AtStartup() &&
mDevice != DeviceManagerDx::Get()->GetCompositorDevice()) { // We use a syncobject to avoid the cost of the mutex lock when // compositing, and because it allows color conversion ocurring directly // from this texture DXVA does not seem to accept IDXGIKeyedMutex textures // as input.
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
layers::ImageBridgeChild::GetSingleton()
->GetTextureFactoryIdentifier()
.mSyncHandle,
mDevice);
}
} else {
mTextureClientAllocator = new D3D11RecycleAllocator(
aKnowsCompositor, mDevice, gfx::SurfaceFormat::NV12);
mKnowsCompositor = aKnowsCompositor; if (StaticPrefs::media_wmf_use_sync_texture_AtStartup()) { // We use a syncobject to avoid the cost of the mutex lock when // compositing, and because it allows color conversion ocurring directly // from this texture DXVA does not seem to accept IDXGIKeyedMutex textures // as input.
mSyncObject = layers::SyncObjectClient::CreateSyncObjectClient(
aKnowsCompositor->GetTextureFactoryIdentifier().mSyncHandle, mDevice);
}
}
mTextureClientAllocator->SetMaxPoolSize(5);
hr = wmf::MFCreateDXGIDeviceManager(&mDeviceManagerToken,
getter_AddRefs(mDXGIDeviceManager)); if (!SUCCEEDED(hr)) {
aFailureReason =
nsPrintfCString("MFCreateDXGIDeviceManager failed with code %lX", hr); return hr;
}
hr = mDXGIDeviceManager->ResetDevice(mDevice, mDeviceManagerToken); if (!SUCCEEDED(hr)) {
aFailureReason = nsPrintfCString( "IMFDXGIDeviceManager::ResetDevice failed with code %lX", hr); return hr;
}
// The IMFTransform interface used by MFTDecoder is documented to require to // run on an MTA thread. // https://msdn.microsoft.com/en-us/library/windows/desktop/ee892371(v=vs.85).aspx#components // The main thread (where this function is called) is STA, not MTA.
RefPtr<MFTDecoder> mft;
mozilla::mscom::EnsureMTA([&]() -> void {
mft = new MFTDecoder();
hr = mft->Create(MFT_CATEGORY_VIDEO_PROCESSOR, MFVideoFormat_NV12,
MFVideoFormat_ARGB32);
if (!SUCCEEDED(hr)) {
aFailureReason = nsPrintfCString( "MFTDecoder::Create of Video Processor MFT for color conversion " "failed with code %lX",
hr); return;
}
hr = mft->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER,
ULONG_PTR(mDXGIDeviceManager.get())); if (!SUCCEEDED(hr)) {
aFailureReason = nsPrintfCString( "MFTDecoder::SendMFTMessage(MFT_MESSAGE_" "SET_D3D_MANAGER) failed with code %lX",
hr); return;
}
});
if (!SUCCEEDED(hr)) { return hr;
}
mTransform = mft;
RefPtr<IDXGIDevice> dxgiDevice;
hr = mDevice->QueryInterface( static_cast<IDXGIDevice**>(getter_AddRefs(dxgiDevice))); if (!SUCCEEDED(hr)) {
aFailureReason =
nsPrintfCString("QI to IDXGIDevice failed with code %lX", hr); return hr;
}
RefPtr<IDXGIAdapter> adapter;
hr = dxgiDevice->GetAdapter(adapter.StartAssignment()); if (!SUCCEEDED(hr)) {
aFailureReason =
nsPrintfCString("IDXGIDevice::GetAdapter failed with code %lX", hr); return hr;
}
DXGI_ADAPTER_DESC adapterDesc;
hr = adapter->GetDesc(&adapterDesc); if (!SUCCEEDED(hr)) {
aFailureReason =
nsPrintfCString("IDXGIAdapter::GetDesc failed with code %lX", hr); return hr;
}
mVendorID = adapterDesc.VendorId;
if ((adapterDesc.VendorId == 0x1022 || adapterDesc.VendorId == 0x1002) &&
!StaticPrefs::media_wmf_skip_blacklist()) { for (constauto& model : sAMDPreUVD4) { if (adapterDesc.DeviceId == model) {
mIsAMDPreUVD4 = true; break;
}
}
}
// Retrieve the DXGI_FORMAT for the current video sample.
RefPtr<IMFMediaBuffer> buffer;
HRESULT hr = aVideoSample->GetBufferByIndex(0, getter_AddRefs(buffer));
NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
void D3D11DXVA2Manager::RefreshIMFSampleWrappers() { for (auto it = mIMFSampleWrappers.begin(); it != mIMFSampleWrappers.end();) { auto wrapper = RefPtr<IMFSampleWrapper>(*it); if (!wrapper) { // wrapper is already destroyed.
it = mIMFSampleWrappers.erase(it); continue;
}
it++;
}
}
void D3D11DXVA2Manager::ReleaseAllIMFSamples() { for (auto it = mIMFSampleWrappers.begin(); it != mIMFSampleWrappers.end();
it++) {
RefPtr<IMFSampleWrapper> wrapper = RefPtr<IMFSampleWrapper>(*it); if (wrapper) {
wrapper->ClearVideoSample();
}
}
}
RefPtr<IDXGIKeyedMutex> mutex;
inTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex)); // The rest of this function will not work if inTexture implements // IDXGIKeyedMutex! In that case case we would have to copy to a // non-mutex using texture.
UINT configCount = 0;
hr = videoDevice->GetVideoDecoderConfigCount(&aDesc, &configCount); if (FAILED(hr)) {
LOG("Failed to get decoder config count!"); returnfalse;
}
for (UINT i = 0; i < configCount; i++) {
D3D11_VIDEO_DECODER_CONFIG config;
hr = videoDevice->GetVideoDecoderConfig(&aDesc, i, &config); if (SUCCEEDED(hr)) {
RefPtr<ID3D11VideoDecoder> decoder;
hr = videoDevice->CreateVideoDecoder(&aDesc, &config,
decoder.StartAssignment()); return decoder != nullptr;
}
} returnfalse;
}
/* static */
DXVA2Manager* DXVA2Manager::CreateD3D11DXVA(
layers::KnowsCompositor* aKnowsCompositor, nsACString& aFailureReason,
ID3D11Device* aDevice, DXVA2Usage aUsage) { // DXVA processing takes up a lot of GPU resources, so limit the number of // videos we use DXVA with at any one time.
uint32_t dxvaLimit = StaticPrefs::media_wmf_dxva_max_videos();
if (sDXVAVideosCount == dxvaLimit && aUsage == DXVA2Usage::Playback) {
aFailureReason.AssignLiteral("Too many DXVA videos playing"); return nullptr;
}
if (!image->AllocateTexture(mTextureClientAllocator, mDevice)) {
LOG("Failed to allocate texture!"); return E_FAIL;
}
RefPtr<TextureClient> client =
image->GetTextureClient(ImageBridgeChild::GetSingleton().get()); if (!client) {
LOG("Failed to get texture client!"); return E_FAIL;
}
UINT height = std::min(inDesc.Height, outDesc.Height);
PerformanceRecorder<PlaybackStage> perfRecorder(
MediaStage::CopyDecodedVideo, height); // The D3D11TextureClientAllocator may return a different texture format // than preferred. In which case the destination texture will be BGRA32. // Eg. when NV12 is blocked by Gfx. if (outDesc.Format == inDesc.Format) { // Our video frame is stored in a non-sharable ID3D11Texture2D. We need // to create a copy of that frame as a sharable resource, save its share // handle, and put that handle into the rendering pipeline.
UINT width = std::min(inDesc.Width, outDesc.Width);
D3D11_BOX srcBox = {0, 0, 0, width, height, 1};
mContext->CopySubresourceRegion(texture, 0, 0, 0, 0, aInTexture.mTexture,
aInTexture.mIndex, &srcBox);
} else { // Convert YUV to RGB. auto* processor = GetOrCreateVideoProcessor(); if (!processor) {
LOG("Failed to get a video processor"); return E_FAIL;
}
VideoProcessorD3D11::InputTextureInfo info(ToColorSpace2(mYUVColorSpace),
mColorRange, aInTexture.mIndex,
aInTexture.mTexture); if (!processor->CallVideoProcessorBlt(info, texture.get())) {
LOG("Failed on CallVideoProcessorBlt!"); return E_FAIL;
}
}
perfRecorder.Record();
}
if (!mutex && mDevice != DeviceManagerDx::Get()->GetCompositorDevice() &&
mSyncObject) { static StaticMutex sMutex MOZ_UNANNOTATED; // Ensure that we only ever attempt to synchronise via the sync object // serially as when using the same D3D11 device for multiple video decoders // it can lead to deadlocks.
StaticMutexAutoLock lock(sMutex); // It appears some race-condition may allow us to arrive here even when // mSyncObject is null. It's better to avoid that crash.
client->SyncWithObject(mSyncObject); if (!mSyncObject->Synchronize(true)) { return DXGI_ERROR_DEVICE_RESET;
}
} elseif (mDevice == DeviceManagerDx::Get()->GetCompositorDevice() &&
mVendorID != 0x8086) {
MOZ_ASSERT(XRE_IsGPUProcess());
MOZ_ASSERT(mVendorID);
// Normally when D3D11Texture2D is copied by // ID3D11DeviceContext::CopySubresourceRegion() with compositor device, // WebRender does not need to wait copy complete, since WebRender also uses // compositor device. But with some non-Intel GPUs, the copy complete need // to be wait explicitly even with compositor device such as when using // video overlays.
auto* data = client->GetInternalData()->AsD3D11TextureData();
MOZ_ASSERT(data); if (data) { // If GPU is not NVIDIA GPU, ID3D11Query is used only for video overlay. // See Bug 1910990 Intel GPU does not use ID3D11Query for waiting video // frame copy. constbool onlyForOverlay = mVendorID != 0x10DE; // Wait query happens only just before blitting for video overlay.
data->RegisterQuery(query, onlyForOverlay);
} else {
gfxCriticalNoteOnce << "D3D11TextureData does not exist";
}
} else {
gfxCriticalNoteOnce << "Could not create D3D11_QUERY_EVENT: "
<< gfx::hexa(hr);
}
}
image.forget(aOutImage); return S_OK;
}
VideoProcessorD3D11* D3D11DXVA2Manager::GetOrCreateVideoProcessor() { if (mProcessor) { return mProcessor;
}
mProcessor = VideoProcessorD3D11::Create(mDevice); if (!mProcessor) {
LOG("Failed to create video processor D3D11"); return nullptr;
}
HRESULT hr = mProcessor->Init(gfx::IntSize(mWidth, mHeight)); if (FAILED(hr)) {
mProcessor = nullptr;
LOG("Failed to init video processor D3D11, hr=%lx", hr);
} return mProcessor;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.