summaryrefslogtreecommitdiffstats
path: root/gfx/layers/D3D11ShareHandleImage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/D3D11ShareHandleImage.cpp')
-rw-r--r--gfx/layers/D3D11ShareHandleImage.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/gfx/layers/D3D11ShareHandleImage.cpp b/gfx/layers/D3D11ShareHandleImage.cpp
new file mode 100644
index 0000000000..edd1711fcf
--- /dev/null
+++ b/gfx/layers/D3D11ShareHandleImage.cpp
@@ -0,0 +1,347 @@
+/* -*- 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 "D3D11ShareHandleImage.h"
+#include <memory>
+#include "DXVA2Manager.h"
+#include "WMF.h"
+#include "d3d11.h"
+#include "gfxImageSurface.h"
+#include "gfxWindowsPlatform.h"
+#include "libyuv.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureD3D11.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+/* static */
+RefPtr<D3D11ShareHandleImage>
+D3D11ShareHandleImage::MaybeCreateNV12ImageAndSetData(
+ KnowsCompositor* aKnowsCompositor, ImageContainer* aContainer,
+ const PlanarYCbCrData& aData) {
+ MOZ_ASSERT(aKnowsCompositor);
+ MOZ_ASSERT(aContainer);
+
+ if (!aKnowsCompositor || !aContainer) {
+ return nullptr;
+ }
+
+ // Check if data could be used with NV12
+ if (aData.YPictureSize().width % 2 != 0 ||
+ aData.YPictureSize().height % 2 != 0 || aData.mYSkip != 0 ||
+ aData.mCbSkip != 0 || aData.mCrSkip != 0 ||
+ aData.mColorDepth != gfx::ColorDepth::COLOR_8 ||
+ aData.mColorRange != gfx::ColorRange::LIMITED ||
+ aData.mChromaSubsampling !=
+ gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) {
+ return nullptr;
+ }
+
+ RefPtr<D3D11ShareHandleImage> image = new D3D11ShareHandleImage(
+ aData.YPictureSize(), aData.mPictureRect,
+ ToColorSpace2(aData.mYUVColorSpace), aData.mColorRange);
+
+ RefPtr<D3D11RecycleAllocator> allocator =
+ aContainer->GetD3D11RecycleAllocator(aKnowsCompositor,
+ gfx::SurfaceFormat::NV12);
+ if (!allocator) {
+ return nullptr;
+ }
+
+ auto syncObject = allocator->GetSyncObject();
+ if (!syncObject) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+ }
+
+ MOZ_ASSERT(allocator->GetUsableSurfaceFormat() == gfx::SurfaceFormat::NV12);
+ if (allocator->GetUsableSurfaceFormat() != gfx::SurfaceFormat::NV12) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Texture2D> stagingTexture =
+ allocator->GetStagingTextureNV12(aData.YPictureSize());
+ if (!stagingTexture) {
+ return nullptr;
+ }
+
+ bool ok = image->AllocateTexture(allocator, allocator->mDevice);
+ if (!ok) {
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> client = image->GetTextureClient(nullptr);
+ if (!client) {
+ return nullptr;
+ }
+
+ // The texture does not have keyed mutex. When keyed mutex exists, the texture
+ // could not be used for video overlay. Then it needs manual synchronization
+ RefPtr<ID3D11Texture2D> texture = image->GetTexture();
+ if (!texture) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ allocator->mDevice->GetImmediateContext(getter_AddRefs(context));
+ if (!context) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D10Multithread> mt;
+ HRESULT hr = allocator->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());
+
+ AutoLockD3D11Texture lockSt(stagingTexture);
+
+ D3D11_MAP mapType = D3D11_MAP_WRITE;
+ D3D11_MAPPED_SUBRESOURCE mappedResource;
+
+ hr = context->Map(stagingTexture, 0, mapType, 0, &mappedResource);
+ if (FAILED(hr)) {
+ gfxCriticalNoteOnce << "Mapping D3D11 staging texture failed: "
+ << gfx::hexa(hr);
+ return nullptr;
+ }
+
+ const size_t destStride = mappedResource.RowPitch;
+ uint8_t* yDestPlaneStart = reinterpret_cast<uint8_t*>(mappedResource.pData);
+ uint8_t* uvDestPlaneStart = reinterpret_cast<uint8_t*>(mappedResource.pData) +
+ destStride * aData.YPictureSize().height;
+ // Convert I420 to NV12,
+ libyuv::I420ToNV12(aData.mYChannel, aData.mYStride, aData.mCbChannel,
+ aData.mCbCrStride, aData.mCrChannel, aData.mCbCrStride,
+ yDestPlaneStart, destStride, uvDestPlaneStart, destStride,
+ aData.YDataSize().width, aData.YDataSize().height);
+
+ context->Unmap(stagingTexture, 0);
+
+ context->CopyResource(texture, stagingTexture);
+
+ context->Flush();
+
+ client->SyncWithObject(syncObject);
+ if (!syncObject->Synchronize(true)) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+ }
+
+ return image;
+}
+
+D3D11ShareHandleImage::D3D11ShareHandleImage(const gfx::IntSize& aSize,
+ const gfx::IntRect& aRect,
+ gfx::ColorSpace2 aColorSpace,
+ gfx::ColorRange aColorRange)
+ : Image(nullptr, ImageFormat::D3D11_SHARE_HANDLE_TEXTURE),
+ mSize(aSize),
+ mPictureRect(aRect),
+ mColorSpace(aColorSpace),
+ mColorRange(aColorRange) {}
+
+bool D3D11ShareHandleImage::AllocateTexture(D3D11RecycleAllocator* aAllocator,
+ ID3D11Device* aDevice) {
+ if (aAllocator) {
+ mTextureClient =
+ aAllocator->CreateOrRecycleClient(mColorSpace, mColorRange, mSize);
+ if (mTextureClient) {
+ D3D11TextureData* textureData = GetData();
+ MOZ_DIAGNOSTIC_ASSERT(textureData, "Wrong TextureDataType");
+ mTexture = textureData->GetD3D11Texture();
+ return true;
+ }
+ return false;
+ } else {
+ MOZ_ASSERT(aDevice);
+ CD3D11_TEXTURE2D_DESC newDesc(
+ DXGI_FORMAT_B8G8R8A8_UNORM, mSize.width, mSize.height, 1, 1,
+ D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
+ newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+
+ HRESULT hr =
+ aDevice->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(mTexture));
+ return SUCCEEDED(hr);
+ }
+}
+
+gfx::IntSize D3D11ShareHandleImage::GetSize() const { return mSize; }
+
+TextureClient* D3D11ShareHandleImage::GetTextureClient(
+ KnowsCompositor* aKnowsCompositor) {
+ return mTextureClient;
+}
+
+already_AddRefed<gfx::SourceSurface>
+D3D11ShareHandleImage::GetAsSourceSurface() {
+ RefPtr<ID3D11Texture2D> src = GetTexture();
+ if (!src) {
+ gfxWarning() << "Cannot readback from shared texture because no texture is "
+ "available.";
+ return nullptr;
+ }
+
+ return gfx::Factory::CreateBGRA8DataSourceSurfaceForD3D11Texture(src);
+}
+
+ID3D11Texture2D* D3D11ShareHandleImage::GetTexture() const { return mTexture; }
+
+class MOZ_RAII D3D11TextureClientAllocationHelper
+ : public ITextureClientAllocationHelper {
+ public:
+ D3D11TextureClientAllocationHelper(gfx::SurfaceFormat aFormat,
+ gfx::ColorSpace2 aColorSpace,
+ gfx::ColorRange aColorRange,
+ const gfx::IntSize& aSize,
+ TextureAllocationFlags aAllocFlags,
+ ID3D11Device* aDevice,
+ TextureFlags aTextureFlags)
+ : ITextureClientAllocationHelper(aFormat, aSize, BackendSelector::Content,
+ aTextureFlags, aAllocFlags),
+ mColorSpace(aColorSpace),
+ mColorRange(aColorRange),
+ mDevice(aDevice) {}
+
+ bool IsCompatible(TextureClient* aTextureClient) override {
+ D3D11TextureData* textureData =
+ aTextureClient->GetInternalData()->AsD3D11TextureData();
+ if (!textureData || aTextureClient->GetFormat() != mFormat ||
+ aTextureClient->GetSize() != mSize) {
+ return false;
+ }
+ // TODO: Should we also check for change in the allocation flags if RGBA?
+ return (aTextureClient->GetFormat() != gfx::SurfaceFormat::NV12 &&
+ aTextureClient->GetFormat() != gfx::SurfaceFormat::P010 &&
+ aTextureClient->GetFormat() != gfx::SurfaceFormat::P016) ||
+ (textureData->mColorSpace == mColorSpace &&
+ textureData->GetColorRange() == mColorRange &&
+ textureData->GetTextureAllocationFlags() == mAllocationFlags);
+ }
+
+ already_AddRefed<TextureClient> Allocate(
+ KnowsCompositor* aAllocator) override {
+ D3D11TextureData* data =
+ D3D11TextureData::Create(mSize, mFormat, mAllocationFlags, mDevice);
+ if (!data) {
+ return nullptr;
+ }
+ data->mColorSpace = mColorSpace;
+ data->SetColorRange(mColorRange);
+ return MakeAndAddRef<TextureClient>(data, mTextureFlags,
+ aAllocator->GetTextureForwarder());
+ }
+
+ private:
+ const gfx::ColorSpace2 mColorSpace;
+ const gfx::ColorRange mColorRange;
+ const RefPtr<ID3D11Device> mDevice;
+};
+
+D3D11RecycleAllocator::D3D11RecycleAllocator(
+ KnowsCompositor* aAllocator, ID3D11Device* aDevice,
+ gfx::SurfaceFormat aPreferredFormat)
+ : TextureClientRecycleAllocator(aAllocator),
+ mDevice(aDevice),
+ mCanUseNV12(StaticPrefs::media_wmf_use_nv12_format() &&
+ gfx::DeviceManagerDx::Get()->CanUseNV12()),
+ mCanUseP010(StaticPrefs::media_wmf_use_nv12_format() &&
+ gfx::DeviceManagerDx::Get()->CanUseP010()),
+ mCanUseP016(StaticPrefs::media_wmf_use_nv12_format() &&
+ gfx::DeviceManagerDx::Get()->CanUseP016()) {
+ SetPreferredSurfaceFormat(aPreferredFormat);
+}
+
+void D3D11RecycleAllocator::SetPreferredSurfaceFormat(
+ gfx::SurfaceFormat aPreferredFormat) {
+ if ((aPreferredFormat == gfx::SurfaceFormat::NV12 && mCanUseNV12) ||
+ (aPreferredFormat == gfx::SurfaceFormat::P010 && mCanUseP010) ||
+ (aPreferredFormat == gfx::SurfaceFormat::P016 && mCanUseP016)) {
+ mUsableSurfaceFormat = aPreferredFormat;
+ return;
+ }
+ // We can't handle the native source format, set it to BGRA which will
+ // force the caller to convert it later.
+ mUsableSurfaceFormat = gfx::SurfaceFormat::B8G8R8A8;
+}
+
+already_AddRefed<TextureClient> D3D11RecycleAllocator::CreateOrRecycleClient(
+ gfx::ColorSpace2 aColorSpace, gfx::ColorRange aColorRange,
+ const gfx::IntSize& aSize) {
+ // When CompositorDevice or ContentDevice is updated,
+ // we could not reuse old D3D11Textures. It could cause video flickering.
+ RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
+ if (!!mImageDevice && mImageDevice != device) {
+ ShrinkToMinimumSize();
+ }
+ mImageDevice = device;
+
+ TextureAllocationFlags allocFlags = TextureAllocationFlags::ALLOC_DEFAULT;
+ if (StaticPrefs::media_wmf_use_sync_texture_AtStartup() ||
+ mDevice == DeviceManagerDx::Get()->GetCompositorDevice()) {
+ // If our device is the compositor device, we don't need any synchronization
+ // in practice.
+ allocFlags = TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION;
+ }
+
+ D3D11TextureClientAllocationHelper helper(
+ mUsableSurfaceFormat, aColorSpace, aColorRange, aSize, allocFlags,
+ mDevice, layers::TextureFlags::DEFAULT);
+
+ RefPtr<TextureClient> textureClient = CreateOrRecycle(helper);
+ return textureClient.forget();
+}
+
+RefPtr<ID3D11Texture2D> D3D11RecycleAllocator::GetStagingTextureNV12(
+ gfx::IntSize aSize) {
+ if (!mStagingTexture || mStagingTextureSize != aSize) {
+ mStagingTexture = nullptr;
+
+ D3D11_TEXTURE2D_DESC desc = {};
+ desc.Width = aSize.width;
+ desc.Height = aSize.height;
+ desc.Format = DXGI_FORMAT_NV12;
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+ desc.SampleDesc.Count = 1;
+
+ HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr,
+ getter_AddRefs(mStagingTexture));
+ if (FAILED(hr)) {
+ gfxCriticalNoteOnce << "allocating D3D11 NV12 staging texture failed: "
+ << gfx::hexa(hr);
+ return nullptr;
+ }
+ MOZ_ASSERT(mStagingTexture);
+ mStagingTextureSize = aSize;
+ }
+
+ return mStagingTexture;
+}
+
+} // namespace layers
+} // namespace mozilla