Bug 1891710: part 2) Enable <Element-outerHTML.html> WPT for Trusted Types. r=smaug
[gecko.git] / gfx / layers / D3D11YCbCrImage.cpp
blobf114ea1d276c636316955f6807f87f6eb6e1d70f
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;
19 namespace mozilla {
20 namespace layers {
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);
38 if (!allocator) {
39 return false;
42 RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetImageDevice();
43 if (!device) {
44 return false;
48 DXGIYCbCrTextureAllocationHelper helper(aData, TextureFlags::DEFAULT,
49 device);
50 mTextureClient = allocator->CreateOrRecycle(helper).unwrapOr(nullptr);
53 if (!mTextureClient) {
54 return false;
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;
69 return false;
72 if (!mt->GetMultithreadProtected()) {
73 gfxCriticalError() << "Device used not marked as multithread-safe.";
74 return false;
77 D3D11MTAutoEnter mtAutoEnter(mt.forget());
79 RefPtr<ID3D11DeviceContext> ctx;
80 device->GetImmediateContext(getter_AddRefs(ctx));
81 if (!ctx) {
82 gfxCriticalError() << "Failed to get immediate context.";
83 return false;
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,
93 aData.mCbCrStride,
94 aData.mCbCrStride * aData.CbCrDataSize().height);
95 ctx->UpdateSubresource(textureCr, 0, nullptr, aData.mCrChannel,
96 aData.mCbCrStride,
97 aData.mCbCrStride * aData.CbCrDataSize().height);
99 return true;
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) {
119 gfxWarning()
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());
127 HRESULT hr;
129 PlanarYCbCrData data;
131 DXGIYCbCrTextureData* dxgiData =
132 mTextureClient->GetInternalData()->AsDXGIYCbCrTextureData();
134 if (!dxgiData) {
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);
169 desc.BindFlags = 0;
170 desc.MiscFlags = 0;
171 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
172 desc.Usage = D3D11_USAGE_STAGING;
174 dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexY));
175 if (!softTexY) {
176 gfxCriticalNote << "Failed to allocate softTexY";
177 return NS_ERROR_FAILURE;
180 texCb->GetDesc(&desc);
181 desc.BindFlags = 0;
182 desc.MiscFlags = 0;
183 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
184 desc.Usage = D3D11_USAGE_STAGING;
186 dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexCb));
187 if (!softTexCb) {
188 gfxCriticalNote << "Failed to allocate softTexCb";
189 return NS_ERROR_FAILURE;
192 texCr->GetDesc(&desc);
193 desc.BindFlags = 0;
194 desc.MiscFlags = 0;
195 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
196 desc.Usage = D3D11_USAGE_STAGING;
198 dev->CreateTexture2D(&desc, nullptr, getter_AddRefs(softTexCr));
199 if (!softTexCr) {
200 gfxCriticalNote << "Failed to allocate softTexCr";
201 return NS_ERROR_FAILURE;
204 RefPtr<ID3D11DeviceContext> ctx;
205 dev->GetImmediateContext(getter_AddRefs(ctx));
206 if (!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);
224 if (FAILED(hr)) {
225 gfxCriticalError() << "Failed to map Y plane (" << hr << ")";
226 return NS_ERROR_FAILURE;
228 hr = ctx->Map(softTexCb, 0, D3D11_MAP_READ, 0, &mapCb);
229 if (FAILED(hr)) {
230 gfxCriticalError() << "Failed to map Y plane (" << hr << ")";
231 return NS_ERROR_FAILURE;
233 hr = ctx->Map(softTexCr, 0, D3D11_MAP_READ, 0, &mapCr);
234 if (FAILED(hr)) {
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);
267 return rv;
270 already_AddRefed<SourceSurface> D3D11YCbCrImage::GetAsSourceSurface() {
271 RefPtr<gfx::DataSourceSurface> surface;
273 nsresult rv =
274 ReadIntoBuffer([&](const PlanarYCbCrData& aData, const IntSize& aSize,
275 SurfaceFormat aFormat) -> nsresult {
276 surface = gfx::Factory::CreateDataSourceSurface(aSize, aFormat);
277 if (!surface) {
278 gfxCriticalError()
279 << "Failed to create DataSourceSurface for image: " << aSize
280 << " " << aFormat;
281 return NS_ERROR_OUT_OF_MEMORY;
284 DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
285 if (!mapping.IsMapped()) {
286 gfxCriticalError()
287 << "Failed to map DataSourceSurface for D3D11YCbCrImage";
288 return NS_ERROR_FAILURE;
291 gfx::ConvertYCbCrToRGB(aData, aFormat, aSize, mapping.GetData(),
292 mapping.GetStride());
293 return NS_OK;
296 if (NS_WARN_IF(NS_FAILED(rv))) {
297 return nullptr;
300 MOZ_ASSERT(surface);
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;
310 int32_t stride = 0;
311 nsresult rv = AllocateSurfaceDescriptorBufferRgb(
312 aSize, aFormat, buffer, aSdBuffer, stride, aAllocate);
313 if (NS_WARN_IF(NS_FAILED(rv))) {
314 return rv;
317 gfx::ConvertYCbCrToRGB(aData, aFormat, aSize, buffer, stride);
318 return NS_OK;
322 class AutoCheckLockD3D11Texture final {
323 public:
324 explicit AutoCheckLockD3D11Texture(ID3D11Texture2D* aTexture)
325 : mIsLocked(false) {
326 aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
327 if (!mMutex) {
328 // If D3D11Texture does not have keyed mutex, we think that the
329 // D3D11Texture could be locked.
330 mIsLocked = true;
331 return;
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) {
337 mIsLocked = true;
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
343 // released.
344 mSyncAcquired = true;
348 ~AutoCheckLockD3D11Texture() {
349 if (!mSyncAcquired) {
350 return;
352 HRESULT hr = mMutex->ReleaseSync(0);
353 if (FAILED(hr)) {
354 NS_WARNING("Failed to unlock the texture");
358 bool IsLocked() const { return mIsLocked; }
360 private:
361 bool 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),
372 mData(aData),
373 mDevice(aDevice) {}
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) {
386 return false;
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()) {
396 return false;
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()) {
407 return false;
410 return true;
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;
429 return nullptr;
432 if (!mt->GetMultithreadProtected()) {
433 gfxCriticalError() << "Device used not marked as multithread-safe.";
434 return nullptr;
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");
468 return nullptr;
471 } // namespace layers
472 } // namespace mozilla