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 "RenderD3D11TextureHost.h"
11 #include "GLContextEGL.h"
12 #include "GLLibraryEGL.h"
13 #include "ScopedGLHelpers.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/gfx/Logging.h"
20 RenderDXGITextureHost::RenderDXGITextureHost(WindowsHandle aHandle
,
21 gfx::SurfaceFormat aFormat
,
22 gfx::YUVColorSpace aYUVColorSpace
,
23 gfx::ColorRange aColorRange
,
30 mYUVColorSpace(aYUVColorSpace
),
31 mColorRange(aColorRange
),
34 MOZ_COUNT_CTOR_INHERITED(RenderDXGITextureHost
, RenderTextureHost
);
35 MOZ_ASSERT((mFormat
!= gfx::SurfaceFormat::NV12
&&
36 mFormat
!= gfx::SurfaceFormat::P010
&&
37 mFormat
!= gfx::SurfaceFormat::P016
) ||
38 (mSize
.width
% 2 == 0 && mSize
.height
% 2 == 0));
42 RenderDXGITextureHost::~RenderDXGITextureHost() {
43 MOZ_COUNT_DTOR_INHERITED(RenderDXGITextureHost
, RenderTextureHost
);
44 DeleteTextureHandle();
47 ID3D11Texture2D
* RenderDXGITextureHost::GetD3D11Texture2DWithGL() {
53 // SingletonGL is always used on Windows with ANGLE.
54 mGL
= RenderThread::Get()->SingletonGL();
57 if (!EnsureD3D11Texture2DWithGL()) {
64 size_t RenderDXGITextureHost::GetPlaneCount() const {
65 if (mFormat
== gfx::SurfaceFormat::NV12
||
66 mFormat
== gfx::SurfaceFormat::P010
||
67 mFormat
== gfx::SurfaceFormat::P016
) {
74 static bool MapTexture(T
* aHost
, RenderCompositor
* aCompositor
,
75 RefPtr
<ID3D11Texture2D
>& aTexture
,
76 RefPtr
<ID3D11DeviceContext
>& aDeviceContext
,
77 RefPtr
<ID3D11Texture2D
>& aCpuTexture
,
78 D3D11_MAPPED_SUBRESOURCE
& aMappedSubresource
) {
83 RenderCompositorD3D11SWGL
* compositor
=
84 aCompositor
->AsRenderCompositorD3D11SWGL();
89 if (!aHost
->EnsureD3D11Texture2D(compositor
->GetDevice())) {
93 if (!aHost
->LockInternal()) {
97 D3D11_TEXTURE2D_DESC textureDesc
= {0};
98 aTexture
->GetDesc(&textureDesc
);
100 compositor
->GetDevice()->GetImmediateContext(getter_AddRefs(aDeviceContext
));
102 textureDesc
.CPUAccessFlags
= D3D11_CPU_ACCESS_READ
;
103 textureDesc
.Usage
= D3D11_USAGE_STAGING
;
104 textureDesc
.BindFlags
= 0;
105 textureDesc
.MiscFlags
= 0;
106 textureDesc
.MipLevels
= 1;
107 HRESULT hr
= compositor
->GetDevice()->CreateTexture2D(
108 &textureDesc
, nullptr, getter_AddRefs(aCpuTexture
));
113 aDeviceContext
->CopyResource(aCpuTexture
, aTexture
);
116 hr
= aDeviceContext
->Map(aCpuTexture
, 0, D3D11_MAP_READ
, 0,
117 &aMappedSubresource
);
118 return SUCCEEDED(hr
);
121 bool RenderDXGITextureHost::MapPlane(RenderCompositor
* aCompositor
,
122 uint8_t aChannelIndex
,
123 PlaneInfo
& aPlaneInfo
) {
124 // TODO: We currently readback from the GPU texture into a new
125 // staging texture every time this is mapped. We might be better
126 // off retaining the mapped memory to trade performance for memory
128 if (!mCpuTexture
&& !MapTexture(this, aCompositor
, mTexture
, mDeviceContext
,
129 mCpuTexture
, mMappedSubresource
)) {
133 aPlaneInfo
.mSize
= GetSize(aChannelIndex
);
134 aPlaneInfo
.mStride
= mMappedSubresource
.RowPitch
;
135 aPlaneInfo
.mData
= mMappedSubresource
.pData
;
137 // If this is the second plane, then offset the data pointer by the
138 // size of the first plane.
139 if (aChannelIndex
== 1) {
141 (uint8_t*)aPlaneInfo
.mData
+ aPlaneInfo
.mStride
* GetSize(0).height
;
146 void RenderDXGITextureHost::UnmapPlanes() {
147 mMappedSubresource
.pData
= nullptr;
149 mDeviceContext
->Unmap(mCpuTexture
, 0);
150 mCpuTexture
= nullptr;
152 mDeviceContext
= nullptr;
155 bool RenderDXGITextureHost::EnsureD3D11Texture2DWithGL() {
160 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
161 const auto& egl
= gle
->mEgl
;
163 // Fetch the D3D11 device.
164 EGLDeviceEXT eglDevice
= nullptr;
165 egl
->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT
, (EGLAttrib
*)&eglDevice
);
166 MOZ_ASSERT(eglDevice
);
167 ID3D11Device
* device
= nullptr;
168 egl
->mLib
->fQueryDeviceAttribEXT(eglDevice
, LOCAL_EGL_D3D11_DEVICE_ANGLE
,
169 (EGLAttrib
*)&device
);
170 // There's a chance this might fail if we end up on d3d9 angle for some
173 gfxCriticalNote
<< "RenderDXGITextureHost device is not available";
177 return EnsureD3D11Texture2D(device
);
180 bool RenderDXGITextureHost::EnsureD3D11Texture2D(ID3D11Device
* aDevice
) {
182 RefPtr
<ID3D11Device
> device
;
183 mTexture
->GetDevice(getter_AddRefs(device
));
184 if (aDevice
!= device
) {
185 gfxCriticalNote
<< "RenderDXGITextureHost uses obsoleted device";
191 // Get the D3D11 texture from shared handle.
192 HRESULT hr
= aDevice
->OpenSharedResource(
193 (HANDLE
)mHandle
, __uuidof(ID3D11Texture2D
),
194 (void**)(ID3D11Texture2D
**)getter_AddRefs(mTexture
));
197 "RenderDXGITextureHost::EnsureLockable(): Failed to open shared "
200 << "RenderDXGITextureHost Failed to open shared texture, hr="
204 MOZ_ASSERT(mTexture
.get());
205 mTexture
->QueryInterface((IDXGIKeyedMutex
**)getter_AddRefs(mKeyedMutex
));
209 bool RenderDXGITextureHost::EnsureLockable(wr::ImageRendering aRendering
) {
210 if (mTextureHandle
[0]) {
211 // Update filter if filter was changed.
212 if (IsFilterUpdateNecessary(aRendering
)) {
213 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE0
,
214 LOCAL_GL_TEXTURE_EXTERNAL_OES
,
215 mTextureHandle
[0], aRendering
);
216 // Cache new rendering filter.
217 mCachedRendering
= aRendering
;
218 // NV12 and P016 uses two handles.
219 if (mFormat
== gfx::SurfaceFormat::NV12
||
220 mFormat
== gfx::SurfaceFormat::P010
||
221 mFormat
== gfx::SurfaceFormat::P016
) {
222 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE1
,
223 LOCAL_GL_TEXTURE_EXTERNAL_OES
,
224 mTextureHandle
[1], aRendering
);
230 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
231 const auto& egl
= gle
->mEgl
;
233 // We use EGLStream to get the converted gl handle from d3d texture. The
234 // NV_stream_consumer_gltexture_yuv and ANGLE_stream_producer_d3d_texture
235 // could support nv12 and rgb d3d texture format.
236 if (!egl
->IsExtensionSupported(
237 gl::EGLExtension::NV_stream_consumer_gltexture_yuv
) ||
238 !egl
->IsExtensionSupported(
239 gl::EGLExtension::ANGLE_stream_producer_d3d_texture
)) {
240 gfxCriticalNote
<< "RenderDXGITextureHost egl extensions are not suppored";
244 // Get the D3D11 texture from shared handle.
245 if (!EnsureD3D11Texture2DWithGL()) {
249 // Create the EGLStream.
250 mStream
= egl
->fCreateStreamKHR(nullptr);
254 if (mFormat
!= gfx::SurfaceFormat::NV12
&&
255 mFormat
!= gfx::SurfaceFormat::P010
&&
256 mFormat
!= gfx::SurfaceFormat::P016
) {
257 // The non-nv12 format.
259 mGL
->fGenTextures(1, mTextureHandle
);
260 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE0
,
261 LOCAL_GL_TEXTURE_EXTERNAL_OES
,
262 mTextureHandle
[0], aRendering
);
263 // Cache new rendering filter.
264 mCachedRendering
= aRendering
;
266 bool(egl
->fStreamConsumerGLTextureExternalAttribsNV(mStream
, nullptr));
267 ok
&= bool(egl
->fCreateStreamProducerD3DTextureANGLE(mStream
, nullptr));
269 // The nv12/p016 format.
271 // Setup the NV12 stream consumer/producer.
272 EGLAttrib consumerAttributes
[] = {
273 LOCAL_EGL_COLOR_BUFFER_TYPE
,
274 LOCAL_EGL_YUV_BUFFER_EXT
,
275 LOCAL_EGL_YUV_NUMBER_OF_PLANES_EXT
,
277 LOCAL_EGL_YUV_PLANE0_TEXTURE_UNIT_NV
,
279 LOCAL_EGL_YUV_PLANE1_TEXTURE_UNIT_NV
,
283 mGL
->fGenTextures(2, mTextureHandle
);
284 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE0
,
285 LOCAL_GL_TEXTURE_EXTERNAL_OES
,
286 mTextureHandle
[0], aRendering
);
287 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE1
,
288 LOCAL_GL_TEXTURE_EXTERNAL_OES
,
289 mTextureHandle
[1], aRendering
);
290 // Cache new rendering filter.
291 mCachedRendering
= aRendering
;
292 ok
&= bool(egl
->fStreamConsumerGLTextureExternalAttribsNV(
293 mStream
, consumerAttributes
));
294 ok
&= bool(egl
->fCreateStreamProducerD3DTextureANGLE(mStream
, nullptr));
297 // Insert the d3d texture.
299 egl
->fStreamPostD3DTextureANGLE(mStream
, (void*)mTexture
.get(), nullptr));
302 gfxCriticalNote
<< "RenderDXGITextureHost init stream failed";
303 DeleteTextureHandle();
307 // Now, we could get the gl handle from the stream.
308 MOZ_ALWAYS_TRUE(egl
->fStreamConsumerAcquireKHR(mStream
));
313 wr::WrExternalImage
RenderDXGITextureHost::Lock(uint8_t aChannelIndex
,
315 wr::ImageRendering aRendering
) {
316 if (mGL
.get() != aGL
) {
317 // Release the texture handle in the previous gl context.
318 DeleteTextureHandle();
323 // XXX Software WebRender is not handled yet.
324 // Software WebRender does not provide GLContext
326 << "Software WebRender is not suppored by RenderDXGITextureHost.";
327 return InvalidToWrExternalImage();
330 if (!EnsureLockable(aRendering
)) {
331 return InvalidToWrExternalImage();
334 if (!LockInternal()) {
335 return InvalidToWrExternalImage();
338 gfx::IntSize size
= GetSize(aChannelIndex
);
339 return NativeTextureToWrExternalImage(GetGLHandle(aChannelIndex
), 0, 0,
340 size
.width
, size
.height
);
343 bool RenderDXGITextureHost::LockInternal() {
346 HRESULT hr
= mKeyedMutex
->AcquireSync(0, 10000);
348 gfxCriticalError() << "RenderDXGITextureHost AcquireSync timeout, hr="
358 void RenderDXGITextureHost::Unlock() {
361 mKeyedMutex
->ReleaseSync(0);
367 void RenderDXGITextureHost::ClearCachedResources() {
368 DeleteTextureHandle();
372 void RenderDXGITextureHost::DeleteTextureHandle() {
373 if (mTextureHandle
[0] == 0) {
377 MOZ_ASSERT(mGL
.get());
382 if (mGL
->MakeCurrent()) {
383 mGL
->fDeleteTextures(2, mTextureHandle
);
385 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
386 const auto& egl
= gle
->mEgl
;
388 egl
->fDestroySurface(mSurface
);
391 egl
->fDestroyStreamKHR(mStream
);
395 for (int i
= 0; i
< 2; ++i
) {
396 mTextureHandle
[i
] = 0;
400 mKeyedMutex
= nullptr;
405 GLuint
RenderDXGITextureHost::GetGLHandle(uint8_t aChannelIndex
) const {
406 MOZ_ASSERT(((mFormat
== gfx::SurfaceFormat::NV12
||
407 mFormat
== gfx::SurfaceFormat::P010
||
408 mFormat
== gfx::SurfaceFormat::P016
) &&
409 aChannelIndex
< 2) ||
411 return mTextureHandle
[aChannelIndex
];
414 gfx::IntSize
RenderDXGITextureHost::GetSize(uint8_t aChannelIndex
) const {
415 MOZ_ASSERT(((mFormat
== gfx::SurfaceFormat::NV12
||
416 mFormat
== gfx::SurfaceFormat::P010
||
417 mFormat
== gfx::SurfaceFormat::P016
) &&
418 aChannelIndex
< 2) ||
421 if (aChannelIndex
== 0) {
424 // The CbCr channel size is a half of Y channel size in NV12 format.
429 RenderDXGIYCbCrTextureHost::RenderDXGIYCbCrTextureHost(
430 WindowsHandle (&aHandles
)[3], gfx::YUVColorSpace aYUVColorSpace
,
431 gfx::ColorDepth aColorDepth
, gfx::ColorRange aColorRange
,
432 gfx::IntSize aSizeY
, gfx::IntSize aSizeCbCr
)
433 : mHandles
{aHandles
[0], aHandles
[1], aHandles
[2]},
437 mYUVColorSpace(aYUVColorSpace
),
438 mColorDepth(aColorDepth
),
439 mColorRange(aColorRange
),
441 mSizeCbCr(aSizeCbCr
),
443 MOZ_COUNT_CTOR_INHERITED(RenderDXGIYCbCrTextureHost
, RenderTextureHost
);
444 // Assume the chroma planes are rounded up if the luma plane is odd sized.
445 MOZ_ASSERT((mSizeCbCr
.width
== mSizeY
.width
||
446 mSizeCbCr
.width
== (mSizeY
.width
+ 1) >> 1) &&
447 (mSizeCbCr
.height
== mSizeY
.height
||
448 mSizeCbCr
.height
== (mSizeY
.height
+ 1) >> 1));
449 MOZ_ASSERT(aHandles
[0] && aHandles
[1] && aHandles
[2]);
452 bool RenderDXGIYCbCrTextureHost::MapPlane(RenderCompositor
* aCompositor
,
453 uint8_t aChannelIndex
,
454 PlaneInfo
& aPlaneInfo
) {
455 D3D11_MAPPED_SUBRESOURCE mappedSubresource
;
456 if (!MapTexture(this, aCompositor
, mTextures
[aChannelIndex
], mDeviceContext
,
457 mCpuTexture
[aChannelIndex
], mappedSubresource
)) {
461 aPlaneInfo
.mSize
= GetSize(aChannelIndex
);
462 aPlaneInfo
.mStride
= mappedSubresource
.RowPitch
;
463 aPlaneInfo
.mData
= mappedSubresource
.pData
;
467 void RenderDXGIYCbCrTextureHost::UnmapPlanes() {
468 for (uint32_t i
= 0; i
< 3; i
++) {
469 if (mCpuTexture
[i
]) {
470 mDeviceContext
->Unmap(mCpuTexture
[i
], 0);
471 mCpuTexture
[i
] = nullptr;
474 mDeviceContext
= nullptr;
477 RenderDXGIYCbCrTextureHost::~RenderDXGIYCbCrTextureHost() {
478 MOZ_COUNT_DTOR_INHERITED(RenderDXGIYCbCrTextureHost
, RenderTextureHost
);
479 DeleteTextureHandle();
482 bool RenderDXGIYCbCrTextureHost::EnsureLockable(wr::ImageRendering aRendering
) {
483 if (mTextureHandles
[0]) {
484 // Update filter if filter was changed.
485 if (IsFilterUpdateNecessary(aRendering
)) {
486 for (int i
= 0; i
< 3; ++i
) {
487 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE0
+ i
,
488 LOCAL_GL_TEXTURE_EXTERNAL_OES
,
489 mTextureHandles
[i
], aRendering
);
490 // Cache new rendering filter.
491 mCachedRendering
= aRendering
;
497 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
498 const auto& egl
= gle
->mEgl
;
500 // The eglCreatePbufferFromClientBuffer doesn't support R8 format, so we
501 // use EGLStream to get the converted gl handle from d3d R8 texture.
503 if (!egl
->IsExtensionSupported(
504 gl::EGLExtension::NV_stream_consumer_gltexture_yuv
) ||
505 !egl
->IsExtensionSupported(
506 gl::EGLExtension::ANGLE_stream_producer_d3d_texture
)) {
508 << "RenderDXGIYCbCrTextureHost egl extensions are not suppored";
512 // Fetch the D3D11 device.
513 EGLDeviceEXT eglDevice
= nullptr;
514 egl
->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT
, (EGLAttrib
*)&eglDevice
);
515 MOZ_ASSERT(eglDevice
);
516 ID3D11Device
* device
= nullptr;
517 egl
->mLib
->fQueryDeviceAttribEXT(eglDevice
, LOCAL_EGL_D3D11_DEVICE_ANGLE
,
518 (EGLAttrib
*)&device
);
519 // There's a chance this might fail if we end up on d3d9 angle for some
522 gfxCriticalNote
<< "RenderDXGIYCbCrTextureHost device is not available";
526 EnsureD3D11Texture2D(device
);
528 mGL
->fGenTextures(3, mTextureHandles
);
530 for (int i
= 0; i
< 3; ++i
) {
531 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE0
+ i
,
532 LOCAL_GL_TEXTURE_EXTERNAL_OES
,
533 mTextureHandles
[i
], aRendering
);
534 // Cache new rendering filter.
535 mCachedRendering
= aRendering
;
537 // Create the EGLStream.
538 mStreams
[i
] = egl
->fCreateStreamKHR(nullptr);
539 MOZ_ASSERT(mStreams
[i
]);
542 egl
->fStreamConsumerGLTextureExternalAttribsNV(mStreams
[i
], nullptr));
543 ok
&= bool(egl
->fCreateStreamProducerD3DTextureANGLE(mStreams
[i
], nullptr));
545 // Insert the R8 texture.
546 ok
&= bool(egl
->fStreamPostD3DTextureANGLE(
547 mStreams
[i
], (void*)mTextures
[i
].get(), nullptr));
549 // Now, we could get the R8 gl handle from the stream.
550 MOZ_ALWAYS_TRUE(egl
->fStreamConsumerAcquireKHR(mStreams
[i
]));
554 gfxCriticalNote
<< "RenderDXGIYCbCrTextureHost init stream failed";
555 DeleteTextureHandle();
562 bool RenderDXGIYCbCrTextureHost::EnsureD3D11Texture2D(ID3D11Device
* aDevice
) {
564 RefPtr
<ID3D11Device
> device
;
565 mTextures
[0]->GetDevice(getter_AddRefs(device
));
566 if (aDevice
!= device
) {
567 gfxCriticalNote
<< "RenderDXGIYCbCrTextureHost uses obsoleted device";
572 if (mTextureHandles
[0]) {
576 for (int i
= 0; i
< 3; ++i
) {
577 // Get the R8 D3D11 texture from shared handle.
578 HRESULT hr
= aDevice
->OpenSharedResource(
579 (HANDLE
)mHandles
[i
], __uuidof(ID3D11Texture2D
),
580 (void**)(ID3D11Texture2D
**)getter_AddRefs(mTextures
[i
]));
583 "RenderDXGIYCbCrTextureHost::EnsureLockable(): Failed to open "
587 << "RenderDXGIYCbCrTextureHost Failed to open shared texture, hr="
593 for (int i
= 0; i
< 3; ++i
) {
594 mTextures
[i
]->QueryInterface(
595 (IDXGIKeyedMutex
**)getter_AddRefs(mKeyedMutexs
[i
]));
600 bool RenderDXGIYCbCrTextureHost::LockInternal() {
602 if (mKeyedMutexs
[0]) {
603 for (const auto& mutex
: mKeyedMutexs
) {
604 HRESULT hr
= mutex
->AcquireSync(0, 10000);
607 << "RenderDXGIYCbCrTextureHost AcquireSync timeout, hr="
618 wr::WrExternalImage
RenderDXGIYCbCrTextureHost::Lock(
619 uint8_t aChannelIndex
, gl::GLContext
* aGL
, wr::ImageRendering aRendering
) {
620 if (mGL
.get() != aGL
) {
621 // Release the texture handle in the previous gl context.
622 DeleteTextureHandle();
627 // XXX Software WebRender is not handled yet.
628 // Software WebRender does not provide GLContext
629 gfxCriticalNoteOnce
<< "Software WebRender is not suppored by "
630 "RenderDXGIYCbCrTextureHost.";
631 return InvalidToWrExternalImage();
634 if (!EnsureLockable(aRendering
)) {
635 return InvalidToWrExternalImage();
638 if (!LockInternal()) {
639 return InvalidToWrExternalImage();
642 gfx::IntSize size
= GetSize(aChannelIndex
);
643 return NativeTextureToWrExternalImage(GetGLHandle(aChannelIndex
), 0, 0,
644 size
.width
, size
.height
);
647 void RenderDXGIYCbCrTextureHost::Unlock() {
649 if (mKeyedMutexs
[0]) {
650 for (const auto& mutex
: mKeyedMutexs
) {
651 mutex
->ReleaseSync(0);
658 void RenderDXGIYCbCrTextureHost::ClearCachedResources() {
659 DeleteTextureHandle();
663 GLuint
RenderDXGIYCbCrTextureHost::GetGLHandle(uint8_t aChannelIndex
) const {
664 MOZ_ASSERT(aChannelIndex
< 3);
666 return mTextureHandles
[aChannelIndex
];
669 gfx::IntSize
RenderDXGIYCbCrTextureHost::GetSize(uint8_t aChannelIndex
) const {
670 MOZ_ASSERT(aChannelIndex
< 3);
672 if (aChannelIndex
== 0) {
679 void RenderDXGIYCbCrTextureHost::DeleteTextureHandle() {
680 if (mTextureHandles
[0] == 0) {
684 MOZ_ASSERT(mGL
.get());
689 if (mGL
->MakeCurrent()) {
690 mGL
->fDeleteTextures(3, mTextureHandles
);
692 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
693 const auto& egl
= gle
->mEgl
;
694 for (int i
= 0; i
< 3; ++i
) {
695 mTextureHandles
[i
] = 0;
696 mTextures
[i
] = nullptr;
697 mKeyedMutexs
[i
] = nullptr;
700 egl
->fDestroySurface(mSurfaces
[i
]);
704 egl
->fDestroyStreamKHR(mStreams
[i
]);
712 } // namespace mozilla