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 "GLBlitHelper.h"
12 #include "GLContextEGL.h"
13 #include "GLLibraryEGL.h"
14 #include "GPUVideoImage.h"
15 #include "ScopedGLHelpers.h"
17 #include "mozilla/gfx/D3D11Checks.h"
18 #include "mozilla/layers/D3D11ShareHandleImage.h"
19 #include "mozilla/layers/D3D11TextureIMFSampleImage.h"
20 #include "mozilla/layers/D3D11YCbCrImage.h"
21 #include "mozilla/layers/GpuProcessD3D11TextureMap.h"
22 #include "mozilla/layers/TextureD3D11.h"
23 #include "mozilla/StaticPrefs_gl.h"
28 #define NOTE_IF_FALSE(expr) \
31 gfxCriticalNote << "NOTE_IF_FALSE: " << #expr; \
35 static EGLStreamKHR
StreamFromD3DTexture(EglDisplay
* const egl
,
36 ID3D11Texture2D
* const texD3D
,
37 const EGLAttrib
* const postAttribs
) {
38 if (!egl
->IsExtensionSupported(
39 EGLExtension::NV_stream_consumer_gltexture_yuv
) ||
40 !egl
->IsExtensionSupported(
41 EGLExtension::ANGLE_stream_producer_d3d_texture
)) {
45 const auto stream
= egl
->fCreateStreamKHR(nullptr);
47 if (!stream
) return 0;
49 NOTE_IF_FALSE(ok
&= bool(egl
->fStreamConsumerGLTextureExternalAttribsNV(
52 ok
&= bool(egl
->fCreateStreamProducerD3DTextureANGLE(stream
, nullptr)));
54 ok
&= bool(egl
->fStreamPostD3DTextureANGLE(stream
, texD3D
, postAttribs
)));
55 if (ok
) return stream
;
57 (void)egl
->fDestroyStreamKHR(stream
);
61 static RefPtr
<ID3D11Texture2D
> OpenSharedTexture(ID3D11Device
* const d3d
,
62 const WindowsHandle handle
) {
63 RefPtr
<ID3D11Device1
> device1
;
64 d3d
->QueryInterface((ID3D11Device1
**)getter_AddRefs(device1
));
66 gfxCriticalNoteOnce
<< "Failed to get ID3D11Device1";
70 RefPtr
<ID3D11Texture2D
> tex
;
71 auto hr
= device1
->OpenSharedResource1(
72 (HANDLE
)handle
, __uuidof(ID3D11Texture2D
),
73 (void**)(ID3D11Texture2D
**)getter_AddRefs(tex
));
75 gfxCriticalError() << "Error code from OpenSharedResource1: "
82 // -------------------------------------
84 class BindAnglePlanes final
{
85 const GLBlitHelper
& mParent
;
86 const uint8_t mNumPlanes
;
87 const ScopedSaveMultiTex mMultiTex
;
89 EGLStreamKHR mStreams
[3];
90 RefPtr
<IDXGIKeyedMutex
> mMutexList
[3];
94 BindAnglePlanes(const GLBlitHelper
* const parent
, const uint8_t numPlanes
,
95 const RefPtr
<ID3D11Texture2D
>* const texD3DList
,
96 const EGLAttrib
* const* postAttribsList
= nullptr)
98 mNumPlanes(numPlanes
),
102 std::vector
<uint8_t> ret
;
103 for (int i
= 0; i
< numPlanes
; i
++) {
108 LOCAL_GL_TEXTURE_EXTERNAL
),
112 MOZ_RELEASE_ASSERT(numPlanes
>= 1 && numPlanes
<= 3);
114 const auto& gl
= mParent
.mGL
;
115 const auto& gle
= GLContextEGL::Cast(gl
);
116 const auto& egl
= gle
->mEgl
;
118 gl
->fGenTextures(numPlanes
, mTempTexs
);
120 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
121 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
122 gl
->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL
, mTempTexs
[i
]);
123 const EGLAttrib
* postAttribs
= nullptr;
124 if (postAttribsList
) {
125 postAttribs
= postAttribsList
[i
];
127 mStreams
[i
] = StreamFromD3DTexture(egl
.get(), texD3DList
[i
], postAttribs
);
128 mSuccess
&= bool(mStreams
[i
]);
132 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
133 NOTE_IF_FALSE(egl
->fStreamConsumerAcquireKHR(mStreams
[i
]));
135 auto& mutex
= mMutexList
[i
];
136 texD3DList
[i
]->QueryInterface(IID_IDXGIKeyedMutex
,
137 (void**)getter_AddRefs(mutex
));
139 const auto hr
= mutex
->AcquireSync(0, 100);
140 if (!gfx::D3D11Checks::DidAcquireSyncSucceed(__func__
, hr
)) {
141 NS_WARNING("BindAnglePlanes failed to acquire KeyedMutex.");
142 NOTE_IF_FALSE(egl
->fStreamConsumerReleaseKHR(mStreams
[i
]));
145 NOTE_IF_FALSE(egl
->fStreamConsumerReleaseKHR(mStreams
[i
]));
147 mMutexList
[i
]->ReleaseSync(0);
159 const auto& gl
= mParent
.mGL
;
160 const auto& gle
= GLContextEGL::Cast(gl
);
161 const auto& egl
= gle
->mEgl
;
164 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
165 NOTE_IF_FALSE(egl
->fStreamConsumerReleaseKHR(mStreams
[i
]));
167 mMutexList
[i
]->ReleaseSync(0);
172 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
173 (void)egl
->fDestroyStreamKHR(mStreams
[i
]);
176 gl
->fDeleteTextures(mNumPlanes
, mTempTexs
);
179 const bool& Success() const { return mSuccess
; }
182 // -------------------------------------
184 ID3D11Device
* GLBlitHelper::GetD3D11() const {
185 if (mD3D11
) return mD3D11
;
187 if (!mGL
->IsANGLE()) return nullptr;
189 const auto& gle
= GLContextEGL::Cast(mGL
);
190 const auto& egl
= gle
->mEgl
;
191 EGLDeviceEXT deviceEGL
= 0;
192 NOTE_IF_FALSE(egl
->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT
,
193 (EGLAttrib
*)&deviceEGL
));
194 ID3D11Device
* device
= nullptr;
195 // ANGLE does not `AddRef` its returned pointer for `QueryDeviceAttrib`, so no
197 if (!egl
->mLib
->fQueryDeviceAttribEXT(deviceEGL
, LOCAL_EGL_D3D11_DEVICE_ANGLE
,
198 (EGLAttrib
*)&device
)) {
199 MOZ_ASSERT(false, "d3d9?");
206 // -------------------------------------
208 bool GLBlitHelper::BlitImage(layers::D3D11ShareHandleImage
* const srcImage
,
209 const gfx::IntSize
& destSize
,
210 const OriginPos destOrigin
) const {
211 const auto& data
= srcImage
->GetData();
212 if (!data
) return false;
214 layers::SurfaceDescriptorD3D10 desc
;
215 if (!data
->SerializeSpecific(&desc
)) return false;
217 return BlitDescriptor(desc
, destSize
, destOrigin
);
220 // -------------------------------------
222 bool GLBlitHelper::BlitImage(layers::D3D11TextureIMFSampleImage
* const srcImage
,
223 const gfx::IntSize
& destSize
,
224 const OriginPos destOrigin
) const {
225 const auto& data
= srcImage
->GetData();
226 if (!data
) return false;
228 layers::SurfaceDescriptorD3D10 desc
;
229 if (!data
->SerializeSpecific(&desc
)) return false;
231 return BlitDescriptor(desc
, destSize
, destOrigin
);
234 // -------------------------------------
236 bool GLBlitHelper::BlitImage(layers::D3D11YCbCrImage
* const srcImage
,
237 const gfx::IntSize
& destSize
,
238 const OriginPos destOrigin
) const {
239 const auto& data
= srcImage
->GetData();
240 if (!data
) return false;
242 const WindowsHandle handles
[3] = {
243 (WindowsHandle
)(data
->mHandles
[0] ? data
->mHandles
[0]->GetHandle()
245 (WindowsHandle
)(data
->mHandles
[1] ? data
->mHandles
[1]->GetHandle()
247 (WindowsHandle
)(data
->mHandles
[2] ? data
->mHandles
[2]->GetHandle()
249 return BlitAngleYCbCr(handles
, srcImage
->mPictureRect
, srcImage
->GetYSize(),
250 srcImage
->GetCbCrSize(), srcImage
->mColorSpace
,
251 destSize
, destOrigin
);
254 // -------------------------------------
256 bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10
& desc
,
257 const gfx::IntSize
& destSize
,
258 const OriginPos destOrigin
) const {
259 const auto& d3d
= GetD3D11();
260 if (!d3d
) return false;
262 const auto& gpuProcessTextureId
= desc
.gpuProcessTextureId();
263 const auto& arrayIndex
= desc
.arrayIndex();
264 const auto& format
= desc
.format();
265 const auto& clipSize
= desc
.size();
267 const auto srcOrigin
= OriginPos::BottomLeft
;
268 const gfx::IntRect
clipRect(0, 0, clipSize
.width
, clipSize
.height
);
269 const auto colorSpace
= desc
.colorSpace();
271 if (format
!= gfx::SurfaceFormat::NV12
&&
272 format
!= gfx::SurfaceFormat::P010
&&
273 format
!= gfx::SurfaceFormat::P016
) {
274 gfxCriticalError() << "Non-NV12 format for SurfaceDescriptorD3D10: "
279 RefPtr
<ID3D11Texture2D
> tex
;
280 if (gpuProcessTextureId
.isSome()) {
281 auto* textureMap
= layers::GpuProcessD3D11TextureMap::Get();
283 Maybe
<HANDLE
> handle
=
284 textureMap
->GetSharedHandleOfCopiedTexture(gpuProcessTextureId
.ref());
285 if (handle
.isSome()) {
286 tex
= OpenSharedTexture(d3d
, (WindowsHandle
)handle
.ref());
289 } else if (desc
.handle()) {
290 tex
= OpenSharedTexture(d3d
, (WindowsHandle
)desc
.handle()->GetHandle());
293 MOZ_GL_ASSERT(mGL
, false); // Get a nullptr from OpenSharedResource1.
296 const RefPtr
<ID3D11Texture2D
> texList
[2] = {tex
, tex
};
297 const EGLAttrib postAttribs0
[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG
, 0,
298 LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE
,
299 static_cast<EGLAttrib
>(arrayIndex
),
301 const EGLAttrib postAttribs1
[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG
, 1,
302 LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE
,
303 static_cast<EGLAttrib
>(arrayIndex
),
305 const EGLAttrib
* const postAttribsList
[2] = {postAttribs0
, postAttribs1
};
306 // /layers/d3d11/CompositorD3D11.cpp uses bt601 for EffectTypes::NV12.
307 // return BlitAngleNv12(tex, YUVColorSpace::BT601, destSize, destOrigin);
309 const BindAnglePlanes
bindPlanes(this, 2, texList
, postAttribsList
);
310 if (!bindPlanes
.Success()) {
311 MOZ_GL_ASSERT(mGL
, false); // BindAnglePlanes failed.
315 D3D11_TEXTURE2D_DESC texDesc
= {0};
316 tex
->GetDesc(&texDesc
);
318 const gfx::IntSize
ySize(texDesc
.Width
, texDesc
.Height
);
319 const gfx::IntSize
divisors(2, 2);
320 MOZ_ASSERT(ySize
.width
% divisors
.width
== 0);
321 MOZ_ASSERT(ySize
.height
% divisors
.height
== 0);
322 const gfx::IntSize
uvSize(ySize
.width
/ divisors
.width
,
323 ySize
.height
/ divisors
.height
);
325 const auto yuvColorSpace
= [&]() {
326 switch (colorSpace
) {
327 case gfx::ColorSpace2::UNKNOWN
:
328 case gfx::ColorSpace2::SRGB
:
329 case gfx::ColorSpace2::DISPLAY_P3
:
330 MOZ_CRASH("Expected BT* colorspace");
331 case gfx::ColorSpace2::BT601_525
:
332 return gfx::YUVColorSpace::BT601
;
333 case gfx::ColorSpace2::BT709
:
334 return gfx::YUVColorSpace::BT709
;
335 case gfx::ColorSpace2::BT2020
:
336 return gfx::YUVColorSpace::BT2020
;
338 MOZ_ASSERT_UNREACHABLE();
341 const bool yFlip
= destOrigin
!= srcOrigin
;
342 const DrawBlitProg::BaseArgs baseArgs
= {SubRectMat3(clipRect
, ySize
), yFlip
,
343 destSize
, Nothing()};
344 const DrawBlitProg::YUVArgs yuvArgs
= {
345 SubRectMat3(clipRect
, uvSize
, divisors
), Some(yuvColorSpace
)};
347 const auto& prog
= GetDrawBlitProg(
348 {kFragHeader_TexExt
, {kFragSample_TwoPlane
, kFragConvert_ColorMatrix
}});
349 prog
->Draw(baseArgs
, &yuvArgs
);
353 bool GLBlitHelper::BlitDescriptor(
354 const layers::SurfaceDescriptorDXGIYCbCr
& desc
,
355 const gfx::IntSize
& destSize
, const OriginPos destOrigin
) const {
356 const auto& clipSize
= desc
.size();
357 const auto& ySize
= desc
.sizeY();
358 const auto& uvSize
= desc
.sizeCbCr();
359 const auto& colorSpace
= desc
.yUVColorSpace();
361 const gfx::IntRect
clipRect(0, 0, clipSize
.width
, clipSize
.height
);
363 auto handleY
= desc
.handleY() ? desc
.handleY()->GetHandle() : nullptr;
364 auto handleCb
= desc
.handleCb() ? desc
.handleCb()->GetHandle() : nullptr;
365 auto handleCr
= desc
.handleCr() ? desc
.handleCr()->GetHandle() : nullptr;
367 const WindowsHandle handles
[3] = {
368 (WindowsHandle
)handleY
, (WindowsHandle
)handleCb
, (WindowsHandle
)handleCr
};
369 return BlitAngleYCbCr(handles
, clipRect
, ySize
, uvSize
, colorSpace
, destSize
,
375 bool GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList
)[3],
376 const gfx::IntRect
& clipRect
,
377 const gfx::IntSize
& ySize
,
378 const gfx::IntSize
& uvSize
,
379 const gfx::YUVColorSpace colorSpace
,
380 const gfx::IntSize
& destSize
,
381 const OriginPos destOrigin
) const {
382 const auto& d3d
= GetD3D11();
383 if (!d3d
) return false;
385 const auto srcOrigin
= OriginPos::BottomLeft
;
387 gfx::IntSize divisors
;
388 if (!GuessDivisors(ySize
, uvSize
, &divisors
)) return false;
390 const RefPtr
<ID3D11Texture2D
> texList
[3] = {
391 OpenSharedTexture(d3d
, handleList
[0]),
392 OpenSharedTexture(d3d
, handleList
[1]),
393 OpenSharedTexture(d3d
, handleList
[2])};
394 const BindAnglePlanes
bindPlanes(this, 3, texList
);
396 const bool yFlip
= destOrigin
!= srcOrigin
;
397 const DrawBlitProg::BaseArgs baseArgs
= {SubRectMat3(clipRect
, ySize
), yFlip
,
398 destSize
, Nothing()};
399 const DrawBlitProg::YUVArgs yuvArgs
= {
400 SubRectMat3(clipRect
, uvSize
, divisors
), Some(colorSpace
)};
402 const auto& prog
= GetDrawBlitProg(
403 {kFragHeader_TexExt
, {kFragSample_ThreePlane
, kFragConvert_ColorMatrix
}});
404 prog
->Draw(baseArgs
, &yuvArgs
);
409 } // namespace mozilla