1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "D3D11ShareHandleImage.h"
9 #include "DXVA2Manager.h"
12 #include "gfxImageSurface.h"
13 #include "gfxWindowsPlatform.h"
15 #include "mozilla/StaticPrefs_media.h"
16 #include "mozilla/gfx/DeviceManagerDx.h"
17 #include "mozilla/layers/CompositableClient.h"
18 #include "mozilla/layers/CompositableForwarder.h"
19 #include "mozilla/layers/TextureClient.h"
20 #include "mozilla/layers/TextureD3D11.h"
28 RefPtr
<D3D11ShareHandleImage
>
29 D3D11ShareHandleImage::MaybeCreateNV12ImageAndSetData(
30 KnowsCompositor
* aKnowsCompositor
, ImageContainer
* aContainer
,
31 const PlanarYCbCrData
& aData
) {
32 MOZ_ASSERT(aKnowsCompositor
);
33 MOZ_ASSERT(aContainer
);
35 if (!aKnowsCompositor
|| !aContainer
) {
39 // Check if data could be used with NV12
40 if (aData
.YPictureSize().width
% 2 != 0 ||
41 aData
.YPictureSize().height
% 2 != 0 || aData
.mYSkip
!= 0 ||
42 aData
.mCbSkip
!= 0 || aData
.mCrSkip
!= 0 ||
43 aData
.mColorDepth
!= gfx::ColorDepth::COLOR_8
||
44 aData
.mColorRange
!= gfx::ColorRange::LIMITED
||
45 aData
.mChromaSubsampling
!=
46 gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT
) {
50 RefPtr
<D3D11ShareHandleImage
> image
= new D3D11ShareHandleImage(
51 aData
.YPictureSize(), aData
.mPictureRect
,
52 ToColorSpace2(aData
.mYUVColorSpace
), aData
.mColorRange
);
54 RefPtr
<D3D11RecycleAllocator
> allocator
=
55 aContainer
->GetD3D11RecycleAllocator(aKnowsCompositor
,
56 gfx::SurfaceFormat::NV12
);
61 auto syncObject
= allocator
->GetSyncObject();
63 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
67 MOZ_ASSERT(allocator
->GetUsableSurfaceFormat() == gfx::SurfaceFormat::NV12
);
68 if (allocator
->GetUsableSurfaceFormat() != gfx::SurfaceFormat::NV12
) {
69 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
73 RefPtr
<ID3D11Texture2D
> stagingTexture
=
74 allocator
->GetStagingTextureNV12(aData
.YPictureSize());
75 if (!stagingTexture
) {
79 bool ok
= image
->AllocateTexture(allocator
, allocator
->mDevice
);
84 RefPtr
<TextureClient
> client
= image
->GetTextureClient(nullptr);
89 client
->AddFlags(TextureFlags::SOFTWARE_DECODED_VIDEO
);
91 // The texture does not have keyed mutex. When keyed mutex exists, the texture
92 // could not be used for video overlay. Then it needs manual synchronization
93 RefPtr
<ID3D11Texture2D
> texture
= image
->GetTexture();
98 RefPtr
<ID3D11DeviceContext
> context
;
99 allocator
->mDevice
->GetImmediateContext(getter_AddRefs(context
));
104 RefPtr
<ID3D10Multithread
> mt
;
105 HRESULT hr
= allocator
->mDevice
->QueryInterface(
106 (ID3D10Multithread
**)getter_AddRefs(mt
));
107 if (FAILED(hr
) || !mt
) {
108 gfxCriticalError() << "Multithread safety interface not supported. " << hr
;
112 if (!mt
->GetMultithreadProtected()) {
113 gfxCriticalError() << "Device used not marked as multithread-safe.";
117 D3D11MTAutoEnter
mtAutoEnter(mt
.forget());
119 AutoLockD3D11Texture
lockSt(stagingTexture
);
121 D3D11_MAP mapType
= D3D11_MAP_WRITE
;
122 D3D11_MAPPED_SUBRESOURCE mappedResource
;
124 hr
= context
->Map(stagingTexture
, 0, mapType
, 0, &mappedResource
);
126 gfxCriticalNoteOnce
<< "Mapping D3D11 staging texture failed: "
131 const size_t destStride
= mappedResource
.RowPitch
;
132 uint8_t* yDestPlaneStart
= reinterpret_cast<uint8_t*>(mappedResource
.pData
);
133 uint8_t* uvDestPlaneStart
= reinterpret_cast<uint8_t*>(mappedResource
.pData
) +
134 destStride
* aData
.YPictureSize().height
;
135 // Convert I420 to NV12,
136 libyuv::I420ToNV12(aData
.mYChannel
, aData
.mYStride
, aData
.mCbChannel
,
137 aData
.mCbCrStride
, aData
.mCrChannel
, aData
.mCbCrStride
,
138 yDestPlaneStart
, destStride
, uvDestPlaneStart
, destStride
,
139 aData
.YDataSize().width
, aData
.YDataSize().height
);
141 context
->Unmap(stagingTexture
, 0);
143 context
->CopyResource(texture
, stagingTexture
);
147 client
->SyncWithObject(syncObject
);
148 if (!syncObject
->Synchronize(true)) {
149 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
156 D3D11ShareHandleImage::D3D11ShareHandleImage(const gfx::IntSize
& aSize
,
157 const gfx::IntRect
& aRect
,
158 gfx::ColorSpace2 aColorSpace
,
159 gfx::ColorRange aColorRange
)
160 : Image(nullptr, ImageFormat::D3D11_SHARE_HANDLE_TEXTURE
),
163 mColorSpace(aColorSpace
),
164 mColorRange(aColorRange
) {}
166 bool D3D11ShareHandleImage::AllocateTexture(D3D11RecycleAllocator
* aAllocator
,
167 ID3D11Device
* aDevice
) {
170 aAllocator
->CreateOrRecycleClient(mColorSpace
, mColorRange
, mSize
);
171 if (mTextureClient
) {
172 D3D11TextureData
* textureData
= GetData();
173 MOZ_DIAGNOSTIC_ASSERT(textureData
, "Wrong TextureDataType");
174 mTexture
= textureData
->GetD3D11Texture();
180 CD3D11_TEXTURE2D_DESC
newDesc(
181 DXGI_FORMAT_B8G8R8A8_UNORM
, mSize
.width
, mSize
.height
, 1, 1,
182 D3D11_BIND_RENDER_TARGET
| D3D11_BIND_SHADER_RESOURCE
);
184 D3D11_RESOURCE_MISC_SHARED_NTHANDLE
| D3D11_RESOURCE_MISC_SHARED
;
187 aDevice
->CreateTexture2D(&newDesc
, nullptr, getter_AddRefs(mTexture
));
188 return SUCCEEDED(hr
);
192 gfx::IntSize
D3D11ShareHandleImage::GetSize() const { return mSize
; }
194 TextureClient
* D3D11ShareHandleImage::GetTextureClient(
195 KnowsCompositor
* aKnowsCompositor
) {
196 return mTextureClient
;
199 already_AddRefed
<gfx::SourceSurface
>
200 D3D11ShareHandleImage::GetAsSourceSurface() {
201 RefPtr
<ID3D11Texture2D
> src
= GetTexture();
203 gfxWarning() << "Cannot readback from shared texture because no texture is "
208 return gfx::Factory::CreateBGRA8DataSourceSurfaceForD3D11Texture(src
);
211 nsresult
D3D11ShareHandleImage::BuildSurfaceDescriptorBuffer(
212 SurfaceDescriptorBuffer
& aSdBuffer
, BuildSdbFlags aFlags
,
213 const std::function
<MemoryOrShmem(uint32_t)>& aAllocate
) {
214 RefPtr
<ID3D11Texture2D
> src
= GetTexture();
216 gfxWarning() << "Cannot readback from shared texture because no texture is "
218 return NS_ERROR_FAILURE
;
221 return gfx::Factory::CreateSdbForD3D11Texture(src
, mSize
, aSdBuffer
,
225 ID3D11Texture2D
* D3D11ShareHandleImage::GetTexture() const { return mTexture
; }
227 class MOZ_RAII D3D11TextureClientAllocationHelper
228 : public ITextureClientAllocationHelper
{
230 D3D11TextureClientAllocationHelper(gfx::SurfaceFormat aFormat
,
231 gfx::ColorSpace2 aColorSpace
,
232 gfx::ColorRange aColorRange
,
233 const gfx::IntSize
& aSize
,
234 TextureAllocationFlags aAllocFlags
,
235 ID3D11Device
* aDevice
,
236 TextureFlags aTextureFlags
)
237 : ITextureClientAllocationHelper(aFormat
, aSize
, BackendSelector::Content
,
238 aTextureFlags
, aAllocFlags
),
239 mColorSpace(aColorSpace
),
240 mColorRange(aColorRange
),
243 bool IsCompatible(TextureClient
* aTextureClient
) override
{
244 D3D11TextureData
* textureData
=
245 aTextureClient
->GetInternalData()->AsD3D11TextureData();
246 if (!textureData
|| aTextureClient
->GetFormat() != mFormat
||
247 aTextureClient
->GetSize() != mSize
) {
250 // TODO: Should we also check for change in the allocation flags if RGBA?
251 return (aTextureClient
->GetFormat() != gfx::SurfaceFormat::NV12
&&
252 aTextureClient
->GetFormat() != gfx::SurfaceFormat::P010
&&
253 aTextureClient
->GetFormat() != gfx::SurfaceFormat::P016
) ||
254 (textureData
->mColorSpace
== mColorSpace
&&
255 textureData
->GetColorRange() == mColorRange
&&
256 textureData
->GetTextureAllocationFlags() == mAllocationFlags
);
259 already_AddRefed
<TextureClient
> Allocate(
260 KnowsCompositor
* aAllocator
) override
{
261 D3D11TextureData
* data
=
262 D3D11TextureData::Create(mSize
, mFormat
, mAllocationFlags
, mDevice
);
266 data
->mColorSpace
= mColorSpace
;
267 data
->SetColorRange(mColorRange
);
268 return MakeAndAddRef
<TextureClient
>(data
, mTextureFlags
,
269 aAllocator
->GetTextureForwarder());
273 const gfx::ColorSpace2 mColorSpace
;
274 const gfx::ColorRange mColorRange
;
275 const RefPtr
<ID3D11Device
> mDevice
;
278 D3D11RecycleAllocator::D3D11RecycleAllocator(
279 KnowsCompositor
* aAllocator
, ID3D11Device
* aDevice
,
280 gfx::SurfaceFormat aPreferredFormat
)
281 : TextureClientRecycleAllocator(aAllocator
),
283 mCanUseNV12(StaticPrefs::media_wmf_use_nv12_format() &&
284 gfx::DeviceManagerDx::Get()->CanUseNV12()),
285 mCanUseP010(StaticPrefs::media_wmf_use_nv12_format() &&
286 gfx::DeviceManagerDx::Get()->CanUseP010()),
287 mCanUseP016(StaticPrefs::media_wmf_use_nv12_format() &&
288 gfx::DeviceManagerDx::Get()->CanUseP016()) {
289 SetPreferredSurfaceFormat(aPreferredFormat
);
292 void D3D11RecycleAllocator::SetPreferredSurfaceFormat(
293 gfx::SurfaceFormat aPreferredFormat
) {
294 if ((aPreferredFormat
== gfx::SurfaceFormat::NV12
&& mCanUseNV12
) ||
295 (aPreferredFormat
== gfx::SurfaceFormat::P010
&& mCanUseP010
) ||
296 (aPreferredFormat
== gfx::SurfaceFormat::P016
&& mCanUseP016
)) {
297 mUsableSurfaceFormat
= aPreferredFormat
;
300 // We can't handle the native source format, set it to BGRA which will
301 // force the caller to convert it later.
302 mUsableSurfaceFormat
= gfx::SurfaceFormat::B8G8R8A8
;
305 already_AddRefed
<TextureClient
> D3D11RecycleAllocator::CreateOrRecycleClient(
306 gfx::ColorSpace2 aColorSpace
, gfx::ColorRange aColorRange
,
307 const gfx::IntSize
& aSize
) {
308 // When CompositorDevice or ContentDevice is updated,
309 // we could not reuse old D3D11Textures. It could cause video flickering.
310 RefPtr
<ID3D11Device
> device
= gfx::DeviceManagerDx::Get()->GetImageDevice();
311 if (!!mImageDevice
&& mImageDevice
!= device
) {
312 ShrinkToMinimumSize();
314 mImageDevice
= device
;
316 TextureAllocationFlags allocFlags
= TextureAllocationFlags::ALLOC_DEFAULT
;
317 if (StaticPrefs::media_wmf_use_sync_texture_AtStartup() ||
318 mDevice
== DeviceManagerDx::Get()->GetCompositorDevice()) {
319 // If our device is the compositor device, we don't need any synchronization
321 allocFlags
= TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION
;
324 D3D11TextureClientAllocationHelper
helper(
325 mUsableSurfaceFormat
, aColorSpace
, aColorRange
, aSize
, allocFlags
,
326 mDevice
, layers::TextureFlags::DEFAULT
);
328 RefPtr
<TextureClient
> textureClient
=
329 CreateOrRecycle(helper
).unwrapOr(nullptr);
330 return textureClient
.forget();
333 RefPtr
<ID3D11Texture2D
> D3D11RecycleAllocator::GetStagingTextureNV12(
334 gfx::IntSize aSize
) {
335 if (!mStagingTexture
|| mStagingTextureSize
!= aSize
) {
336 mStagingTexture
= nullptr;
338 D3D11_TEXTURE2D_DESC desc
= {};
339 desc
.Width
= aSize
.width
;
340 desc
.Height
= aSize
.height
;
341 desc
.Format
= DXGI_FORMAT_NV12
;
344 desc
.Usage
= D3D11_USAGE_STAGING
;
346 desc
.CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
;
348 desc
.SampleDesc
.Count
= 1;
350 HRESULT hr
= mDevice
->CreateTexture2D(&desc
, nullptr,
351 getter_AddRefs(mStagingTexture
));
353 gfxCriticalNoteOnce
<< "allocating D3D11 NV12 staging texture failed: "
357 MOZ_ASSERT(mStagingTexture
);
358 mStagingTextureSize
= aSize
;
361 return mStagingTexture
;
364 } // namespace layers
365 } // namespace mozilla