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 "D3D11YCbCrImage.h"
9 #include "YCbCrUtils.h"
10 #include "gfx2DGlue.h"
11 #include "mozilla/gfx/DeviceManagerDx.h"
12 #include "mozilla/gfx/gfxVars.h"
13 #include "mozilla/layers/CompositableClient.h"
14 #include "mozilla/layers/CompositableForwarder.h"
15 #include "mozilla/layers/TextureD3D11.h"
17 using namespace mozilla::gfx
;
22 D3D11YCbCrImage::D3D11YCbCrImage()
23 : Image(NULL
, ImageFormat::D3D11_YCBCR_IMAGE
) {}
25 D3D11YCbCrImage::~D3D11YCbCrImage() {}
27 bool D3D11YCbCrImage::SetData(KnowsCompositor
* aAllocator
,
28 ImageContainer
* aContainer
,
29 const PlanarYCbCrData
& aData
) {
30 mPictureRect
= aData
.mPictureRect
;
31 mColorDepth
= aData
.mColorDepth
;
32 mColorSpace
= aData
.mYUVColorSpace
;
33 mColorRange
= aData
.mColorRange
;
34 mChromaSubsampling
= aData
.mChromaSubsampling
;
36 RefPtr
<D3D11YCbCrRecycleAllocator
> allocator
=
37 aContainer
->GetD3D11YCbCrRecycleAllocator(aAllocator
);
42 RefPtr
<ID3D11Device
> device
= gfx::DeviceManagerDx::Get()->GetImageDevice();
48 DXGIYCbCrTextureAllocationHelper
helper(aData
, TextureFlags::DEFAULT
,
50 mTextureClient
= allocator
->CreateOrRecycle(helper
).unwrapOr(nullptr);
53 if (!mTextureClient
) {
57 DXGIYCbCrTextureData
* data
=
58 mTextureClient
->GetInternalData()->AsDXGIYCbCrTextureData();
60 ID3D11Texture2D
* textureY
= data
->GetD3D11Texture(0);
61 ID3D11Texture2D
* textureCb
= data
->GetD3D11Texture(1);
62 ID3D11Texture2D
* textureCr
= data
->GetD3D11Texture(2);
64 RefPtr
<ID3D10Multithread
> mt
;
65 HRESULT hr
= device
->QueryInterface((ID3D10Multithread
**)getter_AddRefs(mt
));
67 if (FAILED(hr
) || !mt
) {
68 gfxCriticalError() << "Multithread safety interface not supported. " << hr
;
72 if (!mt
->GetMultithreadProtected()) {
73 gfxCriticalError() << "Device used not marked as multithread-safe.";
77 D3D11MTAutoEnter
mtAutoEnter(mt
.forget());
79 RefPtr
<ID3D11DeviceContext
> ctx
;
80 device
->GetImmediateContext(getter_AddRefs(ctx
));
82 gfxCriticalError() << "Failed to get immediate context.";
86 AutoLockD3D11Texture
lockY(textureY
);
87 AutoLockD3D11Texture
lockCb(textureCb
);
88 AutoLockD3D11Texture
lockCr(textureCr
);
90 ctx
->UpdateSubresource(textureY
, 0, nullptr, aData
.mYChannel
, aData
.mYStride
,
91 aData
.mYStride
* aData
.YDataSize().height
);
92 ctx
->UpdateSubresource(textureCb
, 0, nullptr, aData
.mCbChannel
,
94 aData
.mCbCrStride
* aData
.CbCrDataSize().height
);
95 ctx
->UpdateSubresource(textureCr
, 0, nullptr, aData
.mCrChannel
,
97 aData
.mCbCrStride
* aData
.CbCrDataSize().height
);
102 IntSize
D3D11YCbCrImage::GetSize() const { return mPictureRect
.Size(); }
104 TextureClient
* D3D11YCbCrImage::GetTextureClient(
105 KnowsCompositor
* aKnowsCompositor
) {
106 return mTextureClient
;
109 const DXGIYCbCrTextureData
* D3D11YCbCrImage::GetData() const {
110 if (!mTextureClient
) return nullptr;
112 return mTextureClient
->GetInternalData()->AsDXGIYCbCrTextureData();
115 nsresult
D3D11YCbCrImage::ReadIntoBuffer(
116 const std::function
<nsresult(const PlanarYCbCrData
&, const IntSize
&,
117 SurfaceFormat
)>& aCopy
) {
118 if (!mTextureClient
) {
120 << "GetAsSourceSurface() called on uninitialized D3D11YCbCrImage.";
121 return NS_ERROR_FAILURE
;
124 gfx::IntSize
size(mPictureRect
.Size());
125 gfx::SurfaceFormat format
=
126 gfx::ImageFormatToSurfaceFormat(gfxVars::OffscreenFormat());
129 PlanarYCbCrData data
;
131 DXGIYCbCrTextureData
* dxgiData
=
132 mTextureClient
->GetInternalData()->AsDXGIYCbCrTextureData();
135 gfxCriticalError() << "Failed to get texture client internal data.";
136 return NS_ERROR_FAILURE
;
139 RefPtr
<ID3D11Texture2D
> texY
= dxgiData
->GetD3D11Texture(0);
140 RefPtr
<ID3D11Texture2D
> texCb
= dxgiData
->GetD3D11Texture(1);
141 RefPtr
<ID3D11Texture2D
> texCr
= dxgiData
->GetD3D11Texture(2);
142 RefPtr
<ID3D11Texture2D
> softTexY
, softTexCb
, softTexCr
;
143 D3D11_TEXTURE2D_DESC desc
;
145 RefPtr
<ID3D11Device
> dev
;
146 texY
->GetDevice(getter_AddRefs(dev
));
148 if (!dev
|| dev
!= gfx::DeviceManagerDx::Get()->GetImageDevice()) {
149 gfxCriticalError() << "D3D11Device is obsoleted";
150 return NS_ERROR_FAILURE
;
153 RefPtr
<ID3D10Multithread
> mt
;
154 hr
= dev
->QueryInterface((ID3D10Multithread
**)getter_AddRefs(mt
));
156 if (FAILED(hr
) || !mt
) {
157 gfxCriticalError() << "Multithread safety interface not supported.";
158 return NS_ERROR_FAILURE
;
161 if (!mt
->GetMultithreadProtected()) {
162 gfxCriticalError() << "Device used not marked as multithread-safe.";
163 return NS_ERROR_FAILURE
;
166 D3D11MTAutoEnter
mtAutoEnter(mt
.forget());
168 texY
->GetDesc(&desc
);
171 desc
.CPUAccessFlags
= D3D11_CPU_ACCESS_READ
;
172 desc
.Usage
= D3D11_USAGE_STAGING
;
174 dev
->CreateTexture2D(&desc
, nullptr, getter_AddRefs(softTexY
));
176 gfxCriticalNote
<< "Failed to allocate softTexY";
177 return NS_ERROR_FAILURE
;
180 texCb
->GetDesc(&desc
);
183 desc
.CPUAccessFlags
= D3D11_CPU_ACCESS_READ
;
184 desc
.Usage
= D3D11_USAGE_STAGING
;
186 dev
->CreateTexture2D(&desc
, nullptr, getter_AddRefs(softTexCb
));
188 gfxCriticalNote
<< "Failed to allocate softTexCb";
189 return NS_ERROR_FAILURE
;
192 texCr
->GetDesc(&desc
);
195 desc
.CPUAccessFlags
= D3D11_CPU_ACCESS_READ
;
196 desc
.Usage
= D3D11_USAGE_STAGING
;
198 dev
->CreateTexture2D(&desc
, nullptr, getter_AddRefs(softTexCr
));
200 gfxCriticalNote
<< "Failed to allocate softTexCr";
201 return NS_ERROR_FAILURE
;
204 RefPtr
<ID3D11DeviceContext
> ctx
;
205 dev
->GetImmediateContext(getter_AddRefs(ctx
));
207 gfxCriticalError() << "Failed to get immediate context.";
208 return NS_ERROR_FAILURE
;
212 AutoLockD3D11Texture
lockY(texY
);
213 AutoLockD3D11Texture
lockCb(texCb
);
214 AutoLockD3D11Texture
lockCr(texCr
);
215 ctx
->CopyResource(softTexY
, texY
);
216 ctx
->CopyResource(softTexCb
, texCb
);
217 ctx
->CopyResource(softTexCr
, texCr
);
220 D3D11_MAPPED_SUBRESOURCE mapY
, mapCb
, mapCr
;
221 mapY
.pData
= mapCb
.pData
= mapCr
.pData
= nullptr;
223 hr
= ctx
->Map(softTexY
, 0, D3D11_MAP_READ
, 0, &mapY
);
225 gfxCriticalError() << "Failed to map Y plane (" << hr
<< ")";
226 return NS_ERROR_FAILURE
;
228 hr
= ctx
->Map(softTexCb
, 0, D3D11_MAP_READ
, 0, &mapCb
);
230 gfxCriticalError() << "Failed to map Y plane (" << hr
<< ")";
231 return NS_ERROR_FAILURE
;
233 hr
= ctx
->Map(softTexCr
, 0, D3D11_MAP_READ
, 0, &mapCr
);
235 gfxCriticalError() << "Failed to map Y plane (" << hr
<< ")";
236 return NS_ERROR_FAILURE
;
239 MOZ_ASSERT(mapCb
.RowPitch
== mapCr
.RowPitch
);
241 data
.mPictureRect
= mPictureRect
;
242 data
.mStereoMode
= StereoMode::MONO
;
243 data
.mColorDepth
= mColorDepth
;
244 data
.mYUVColorSpace
= mColorSpace
;
245 data
.mColorRange
= mColorRange
;
246 data
.mChromaSubsampling
= mChromaSubsampling
;
247 data
.mYSkip
= data
.mCbSkip
= data
.mCrSkip
= 0;
248 data
.mYChannel
= static_cast<uint8_t*>(mapY
.pData
);
249 data
.mYStride
= mapY
.RowPitch
;
250 data
.mCbChannel
= static_cast<uint8_t*>(mapCb
.pData
);
251 data
.mCrChannel
= static_cast<uint8_t*>(mapCr
.pData
);
252 data
.mCbCrStride
= mapCb
.RowPitch
;
254 gfx::GetYCbCrToRGBDestFormatAndSize(data
, format
, size
);
255 if (size
.width
> PlanarYCbCrImage::MAX_DIMENSION
||
256 size
.height
> PlanarYCbCrImage::MAX_DIMENSION
) {
257 gfxCriticalError() << "Illegal image dest width or height";
258 return NS_ERROR_FAILURE
;
261 nsresult rv
= aCopy(data
, size
, format
);
263 ctx
->Unmap(softTexY
, 0);
264 ctx
->Unmap(softTexCb
, 0);
265 ctx
->Unmap(softTexCr
, 0);
270 already_AddRefed
<SourceSurface
> D3D11YCbCrImage::GetAsSourceSurface() {
271 RefPtr
<gfx::DataSourceSurface
> surface
;
274 ReadIntoBuffer([&](const PlanarYCbCrData
& aData
, const IntSize
& aSize
,
275 SurfaceFormat aFormat
) -> nsresult
{
276 surface
= gfx::Factory::CreateDataSourceSurface(aSize
, aFormat
);
279 << "Failed to create DataSourceSurface for image: " << aSize
281 return NS_ERROR_OUT_OF_MEMORY
;
284 DataSourceSurface::ScopedMap
mapping(surface
, DataSourceSurface::WRITE
);
285 if (!mapping
.IsMapped()) {
287 << "Failed to map DataSourceSurface for D3D11YCbCrImage";
288 return NS_ERROR_FAILURE
;
291 gfx::ConvertYCbCrToRGB(aData
, aFormat
, aSize
, mapping
.GetData(),
292 mapping
.GetStride());
296 if (NS_WARN_IF(NS_FAILED(rv
))) {
301 return surface
.forget();
304 nsresult
D3D11YCbCrImage::BuildSurfaceDescriptorBuffer(
305 SurfaceDescriptorBuffer
& aSdBuffer
, BuildSdbFlags aFlags
,
306 const std::function
<MemoryOrShmem(uint32_t)>& aAllocate
) {
307 return ReadIntoBuffer([&](const PlanarYCbCrData
& aData
, const IntSize
& aSize
,
308 SurfaceFormat aFormat
) -> nsresult
{
309 uint8_t* buffer
= nullptr;
311 nsresult rv
= AllocateSurfaceDescriptorBufferRgb(
312 aSize
, aFormat
, buffer
, aSdBuffer
, stride
, aAllocate
);
313 if (NS_WARN_IF(NS_FAILED(rv
))) {
317 gfx::ConvertYCbCrToRGB(aData
, aFormat
, aSize
, buffer
, stride
);
322 class AutoCheckLockD3D11Texture final
{
324 explicit AutoCheckLockD3D11Texture(ID3D11Texture2D
* aTexture
)
326 aTexture
->QueryInterface((IDXGIKeyedMutex
**)getter_AddRefs(mMutex
));
328 // If D3D11Texture does not have keyed mutex, we think that the
329 // D3D11Texture could be locked.
334 // Test to see if the keyed mutex has been released
335 HRESULT hr
= mMutex
->AcquireSync(0, 0);
336 if (hr
== S_OK
|| hr
== WAIT_ABANDONED
) {
338 // According to Microsoft documentation:
339 // WAIT_ABANDONED - The shared surface and keyed mutex are no longer in a
340 // consistent state. If AcquireSync returns this value, you should release
341 // and recreate both the keyed mutex and the shared surface
342 // So even if we do get WAIT_ABANDONED, the keyed mutex will have to be
344 mSyncAcquired
= true;
348 ~AutoCheckLockD3D11Texture() {
349 if (!mSyncAcquired
) {
352 HRESULT hr
= mMutex
->ReleaseSync(0);
354 NS_WARNING("Failed to unlock the texture");
358 bool IsLocked() const { return mIsLocked
; }
362 bool mSyncAcquired
= false;
363 RefPtr
<IDXGIKeyedMutex
> mMutex
;
366 DXGIYCbCrTextureAllocationHelper::DXGIYCbCrTextureAllocationHelper(
367 const PlanarYCbCrData
& aData
, TextureFlags aTextureFlags
,
368 ID3D11Device
* aDevice
)
369 : ITextureClientAllocationHelper(
370 gfx::SurfaceFormat::YUV
, aData
.mPictureRect
.Size(),
371 BackendSelector::Content
, aTextureFlags
, ALLOC_DEFAULT
),
375 bool DXGIYCbCrTextureAllocationHelper::IsCompatible(
376 TextureClient
* aTextureClient
) {
377 MOZ_ASSERT(aTextureClient
->GetFormat() == gfx::SurfaceFormat::YUV
);
379 DXGIYCbCrTextureData
* dxgiData
=
380 aTextureClient
->GetInternalData()->AsDXGIYCbCrTextureData();
381 if (!dxgiData
|| aTextureClient
->GetSize() != mData
.mPictureRect
.Size() ||
382 dxgiData
->GetYSize() != mData
.YDataSize() ||
383 dxgiData
->GetCbCrSize() != mData
.CbCrDataSize() ||
384 dxgiData
->GetColorDepth() != mData
.mColorDepth
||
385 dxgiData
->GetYUVColorSpace() != mData
.mYUVColorSpace
) {
389 ID3D11Texture2D
* textureY
= dxgiData
->GetD3D11Texture(0);
390 ID3D11Texture2D
* textureCb
= dxgiData
->GetD3D11Texture(1);
391 ID3D11Texture2D
* textureCr
= dxgiData
->GetD3D11Texture(2);
393 RefPtr
<ID3D11Device
> device
;
394 textureY
->GetDevice(getter_AddRefs(device
));
395 if (!device
|| device
!= gfx::DeviceManagerDx::Get()->GetImageDevice()) {
399 // Test to see if the keyed mutex has been released.
400 // If D3D11Texture failed to lock, do not recycle the DXGIYCbCrTextureData.
402 AutoCheckLockD3D11Texture
lockY(textureY
);
403 AutoCheckLockD3D11Texture
lockCr(textureCr
);
404 AutoCheckLockD3D11Texture
lockCb(textureCb
);
406 if (!lockY
.IsLocked() || !lockCr
.IsLocked() || !lockCb
.IsLocked()) {
413 already_AddRefed
<TextureClient
> DXGIYCbCrTextureAllocationHelper::Allocate(
414 KnowsCompositor
* aAllocator
) {
415 auto ySize
= mData
.YDataSize();
416 auto cbcrSize
= mData
.CbCrDataSize();
417 CD3D11_TEXTURE2D_DESC
newDesc(mData
.mColorDepth
== gfx::ColorDepth::COLOR_8
418 ? DXGI_FORMAT_R8_UNORM
419 : DXGI_FORMAT_R16_UNORM
,
420 ySize
.width
, ySize
.height
, 1, 1);
421 newDesc
.MiscFlags
= D3D11_RESOURCE_MISC_SHARED_NTHANDLE
|
422 D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
;
424 RefPtr
<ID3D10Multithread
> mt
;
425 HRESULT hr
= mDevice
->QueryInterface((ID3D10Multithread
**)getter_AddRefs(mt
));
427 if (FAILED(hr
) || !mt
) {
428 gfxCriticalError() << "Multithread safety interface not supported. " << hr
;
432 if (!mt
->GetMultithreadProtected()) {
433 gfxCriticalError() << "Device used not marked as multithread-safe.";
437 D3D11MTAutoEnter
mtAutoEnter(mt
.forget());
439 RefPtr
<ID3D11Texture2D
> textureY
;
440 hr
= mDevice
->CreateTexture2D(&newDesc
, nullptr, getter_AddRefs(textureY
));
441 NS_ENSURE_TRUE(SUCCEEDED(hr
), nullptr);
443 newDesc
.Width
= cbcrSize
.width
;
444 newDesc
.Height
= cbcrSize
.height
;
446 RefPtr
<ID3D11Texture2D
> textureCb
;
447 hr
= mDevice
->CreateTexture2D(&newDesc
, nullptr, getter_AddRefs(textureCb
));
448 NS_ENSURE_TRUE(SUCCEEDED(hr
), nullptr);
450 RefPtr
<ID3D11Texture2D
> textureCr
;
451 hr
= mDevice
->CreateTexture2D(&newDesc
, nullptr, getter_AddRefs(textureCr
));
452 NS_ENSURE_TRUE(SUCCEEDED(hr
), nullptr);
454 TextureForwarder
* forwarder
=
455 aAllocator
? aAllocator
->GetTextureForwarder() : nullptr;
457 return TextureClient::CreateWithData(
458 DXGIYCbCrTextureData::Create(
459 textureY
, textureCb
, textureCr
, mData
.mPictureRect
.Size(), ySize
,
460 cbcrSize
, mData
.mColorDepth
, mData
.mYUVColorSpace
, mData
.mColorRange
),
461 mTextureFlags
, forwarder
);
464 already_AddRefed
<TextureClient
> D3D11YCbCrRecycleAllocator::Allocate(
465 SurfaceFormat aFormat
, IntSize aSize
, BackendSelector aSelector
,
466 TextureFlags aTextureFlags
, TextureAllocationFlags aAllocFlags
) {
467 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
471 } // namespace layers
472 } // namespace mozilla