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/layers/D3D11ShareHandleImage.h"
18 #include "mozilla/layers/D3D11TextureIMFSampleImage.h"
19 #include "mozilla/layers/D3D11YCbCrImage.h"
20 #include "mozilla/layers/GpuProcessD3D11TextureMap.h"
21 #include "mozilla/layers/TextureD3D11.h"
22 #include "mozilla/StaticPrefs_gl.h"
27 #define NOTE_IF_FALSE(expr) \
30 gfxCriticalNote << "NOTE_IF_FALSE: " << #expr; \
34 static EGLStreamKHR
StreamFromD3DTexture(EglDisplay
* const egl
,
35 ID3D11Texture2D
* const texD3D
,
36 const EGLAttrib
* const postAttribs
) {
37 if (!egl
->IsExtensionSupported(
38 EGLExtension::NV_stream_consumer_gltexture_yuv
) ||
39 !egl
->IsExtensionSupported(
40 EGLExtension::ANGLE_stream_producer_d3d_texture
)) {
44 const auto stream
= egl
->fCreateStreamKHR(nullptr);
46 if (!stream
) return 0;
48 NOTE_IF_FALSE(ok
&= bool(egl
->fStreamConsumerGLTextureExternalAttribsNV(
51 ok
&= bool(egl
->fCreateStreamProducerD3DTextureANGLE(stream
, nullptr)));
53 ok
&= bool(egl
->fStreamPostD3DTextureANGLE(stream
, texD3D
, postAttribs
)));
54 if (ok
) return stream
;
56 (void)egl
->fDestroyStreamKHR(stream
);
60 static RefPtr
<ID3D11Texture2D
> OpenSharedTexture(ID3D11Device
* const d3d
,
61 const WindowsHandle handle
) {
62 RefPtr
<ID3D11Device1
> device1
;
63 d3d
->QueryInterface((ID3D11Device1
**)getter_AddRefs(device1
));
65 gfxCriticalNoteOnce
<< "Failed to get ID3D11Device1";
69 RefPtr
<ID3D11Texture2D
> tex
;
70 auto hr
= device1
->OpenSharedResource1(
71 (HANDLE
)handle
, __uuidof(ID3D11Texture2D
),
72 (void**)(ID3D11Texture2D
**)getter_AddRefs(tex
));
74 gfxCriticalError() << "Error code from OpenSharedResource1: "
81 // -------------------------------------
83 class BindAnglePlanes final
{
84 const GLBlitHelper
& mParent
;
85 const uint8_t mNumPlanes
;
86 const ScopedSaveMultiTex mMultiTex
;
88 EGLStreamKHR mStreams
[3];
89 RefPtr
<IDXGIKeyedMutex
> mMutexList
[3];
93 BindAnglePlanes(const GLBlitHelper
* const parent
, const uint8_t numPlanes
,
94 const RefPtr
<ID3D11Texture2D
>* const texD3DList
,
95 const EGLAttrib
* const* postAttribsList
= nullptr)
97 mNumPlanes(numPlanes
),
101 std::vector
<uint8_t> ret
;
102 for (int i
= 0; i
< numPlanes
; i
++) {
107 LOCAL_GL_TEXTURE_EXTERNAL
),
111 MOZ_RELEASE_ASSERT(numPlanes
>= 1 && numPlanes
<= 3);
113 const auto& gl
= mParent
.mGL
;
114 const auto& gle
= GLContextEGL::Cast(gl
);
115 const auto& egl
= gle
->mEgl
;
117 gl
->fGenTextures(numPlanes
, mTempTexs
);
119 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
120 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
+ i
);
121 gl
->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL
, mTempTexs
[i
]);
122 const EGLAttrib
* postAttribs
= nullptr;
123 if (postAttribsList
) {
124 postAttribs
= postAttribsList
[i
];
126 mStreams
[i
] = StreamFromD3DTexture(egl
.get(), texD3DList
[i
], postAttribs
);
127 mSuccess
&= bool(mStreams
[i
]);
131 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
132 NOTE_IF_FALSE(egl
->fStreamConsumerAcquireKHR(mStreams
[i
]));
134 auto& mutex
= mMutexList
[i
];
135 texD3DList
[i
]->QueryInterface(IID_IDXGIKeyedMutex
,
136 (void**)getter_AddRefs(mutex
));
138 const auto hr
= mutex
->AcquireSync(0, 100);
140 NS_WARNING("BindAnglePlanes failed to acquire KeyedMutex.");
149 const auto& gl
= mParent
.mGL
;
150 const auto& gle
= GLContextEGL::Cast(gl
);
151 const auto& egl
= gle
->mEgl
;
154 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
155 NOTE_IF_FALSE(egl
->fStreamConsumerReleaseKHR(mStreams
[i
]));
157 mMutexList
[i
]->ReleaseSync(0);
162 for (uint8_t i
= 0; i
< mNumPlanes
; i
++) {
163 (void)egl
->fDestroyStreamKHR(mStreams
[i
]);
166 gl
->fDeleteTextures(mNumPlanes
, mTempTexs
);
169 const bool& Success() const { return mSuccess
; }
172 // -------------------------------------
174 ID3D11Device
* GLBlitHelper::GetD3D11() const {
175 if (mD3D11
) return mD3D11
;
177 if (!mGL
->IsANGLE()) return nullptr;
179 const auto& gle
= GLContextEGL::Cast(mGL
);
180 const auto& egl
= gle
->mEgl
;
181 EGLDeviceEXT deviceEGL
= 0;
182 NOTE_IF_FALSE(egl
->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT
,
183 (EGLAttrib
*)&deviceEGL
));
184 ID3D11Device
* device
= nullptr;
185 // ANGLE does not `AddRef` its returned pointer for `QueryDeviceAttrib`, so no
187 if (!egl
->mLib
->fQueryDeviceAttribEXT(deviceEGL
, LOCAL_EGL_D3D11_DEVICE_ANGLE
,
188 (EGLAttrib
*)&device
)) {
189 MOZ_ASSERT(false, "d3d9?");
196 // -------------------------------------
198 bool GLBlitHelper::BlitImage(layers::D3D11ShareHandleImage
* const srcImage
,
199 const gfx::IntSize
& destSize
,
200 const OriginPos destOrigin
) const {
201 const auto& data
= srcImage
->GetData();
202 if (!data
) return false;
204 layers::SurfaceDescriptorD3D10 desc
;
205 if (!data
->SerializeSpecific(&desc
)) return false;
207 return BlitDescriptor(desc
, destSize
, destOrigin
);
210 // -------------------------------------
212 bool GLBlitHelper::BlitImage(layers::D3D11TextureIMFSampleImage
* const srcImage
,
213 const gfx::IntSize
& destSize
,
214 const OriginPos destOrigin
) const {
215 const auto& data
= srcImage
->GetData();
216 if (!data
) return false;
218 layers::SurfaceDescriptorD3D10 desc
;
219 if (!data
->SerializeSpecific(&desc
)) return false;
221 return BlitDescriptor(desc
, destSize
, destOrigin
);
224 // -------------------------------------
226 bool GLBlitHelper::BlitImage(layers::D3D11YCbCrImage
* const srcImage
,
227 const gfx::IntSize
& destSize
,
228 const OriginPos destOrigin
) const {
229 const auto& data
= srcImage
->GetData();
230 if (!data
) return false;
232 const WindowsHandle handles
[3] = {
233 (WindowsHandle
)(data
->mHandles
[0] ? data
->mHandles
[0]->GetHandle()
235 (WindowsHandle
)(data
->mHandles
[1] ? data
->mHandles
[1]->GetHandle()
237 (WindowsHandle
)(data
->mHandles
[2] ? data
->mHandles
[2]->GetHandle()
239 return BlitAngleYCbCr(handles
, srcImage
->mPictureRect
, srcImage
->GetYSize(),
240 srcImage
->GetCbCrSize(), srcImage
->mColorSpace
,
241 destSize
, destOrigin
);
244 // -------------------------------------
246 bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10
& desc
,
247 const gfx::IntSize
& destSize
,
248 const OriginPos destOrigin
) const {
249 const auto& d3d
= GetD3D11();
250 if (!d3d
) return false;
252 const auto& gpuProcessTextureId
= desc
.gpuProcessTextureId();
253 const auto& arrayIndex
= desc
.arrayIndex();
254 const auto& format
= desc
.format();
255 const auto& clipSize
= desc
.size();
257 const auto srcOrigin
= OriginPos::BottomLeft
;
258 const gfx::IntRect
clipRect(0, 0, clipSize
.width
, clipSize
.height
);
259 const auto colorSpace
= desc
.colorSpace();
261 if (format
!= gfx::SurfaceFormat::NV12
&&
262 format
!= gfx::SurfaceFormat::P010
&&
263 format
!= gfx::SurfaceFormat::P016
) {
264 gfxCriticalError() << "Non-NV12 format for SurfaceDescriptorD3D10: "
269 RefPtr
<ID3D11Texture2D
> tex
;
270 if (gpuProcessTextureId
.isSome()) {
271 auto* textureMap
= layers::GpuProcessD3D11TextureMap::Get();
273 Maybe
<HANDLE
> handle
=
274 textureMap
->GetSharedHandleOfCopiedTexture(gpuProcessTextureId
.ref());
275 if (handle
.isSome()) {
276 tex
= OpenSharedTexture(d3d
, (WindowsHandle
)handle
.ref());
279 } else if (desc
.handle()) {
280 tex
= OpenSharedTexture(d3d
, (WindowsHandle
)desc
.handle()->GetHandle());
283 MOZ_GL_ASSERT(mGL
, false); // Get a nullptr from OpenSharedResource1.
286 const RefPtr
<ID3D11Texture2D
> texList
[2] = {tex
, tex
};
287 const EGLAttrib postAttribs0
[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG
, 0,
288 LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE
,
289 static_cast<EGLAttrib
>(arrayIndex
),
291 const EGLAttrib postAttribs1
[] = {LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG
, 1,
292 LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE
,
293 static_cast<EGLAttrib
>(arrayIndex
),
295 const EGLAttrib
* const postAttribsList
[2] = {postAttribs0
, postAttribs1
};
296 // /layers/d3d11/CompositorD3D11.cpp uses bt601 for EffectTypes::NV12.
297 // return BlitAngleNv12(tex, YUVColorSpace::BT601, destSize, destOrigin);
299 const BindAnglePlanes
bindPlanes(this, 2, texList
, postAttribsList
);
300 if (!bindPlanes
.Success()) {
301 MOZ_GL_ASSERT(mGL
, false); // BindAnglePlanes failed.
305 D3D11_TEXTURE2D_DESC texDesc
= {0};
306 tex
->GetDesc(&texDesc
);
308 const gfx::IntSize
ySize(texDesc
.Width
, texDesc
.Height
);
309 const gfx::IntSize
divisors(2, 2);
310 MOZ_ASSERT(ySize
.width
% divisors
.width
== 0);
311 MOZ_ASSERT(ySize
.height
% divisors
.height
== 0);
312 const gfx::IntSize
uvSize(ySize
.width
/ divisors
.width
,
313 ySize
.height
/ divisors
.height
);
315 const auto yuvColorSpace
= [&]() {
316 switch (colorSpace
) {
317 case gfx::ColorSpace2::UNKNOWN
:
318 case gfx::ColorSpace2::SRGB
:
319 case gfx::ColorSpace2::DISPLAY_P3
:
320 MOZ_CRASH("Expected BT* colorspace");
321 case gfx::ColorSpace2::BT601_525
:
322 return gfx::YUVColorSpace::BT601
;
323 case gfx::ColorSpace2::BT709
:
324 return gfx::YUVColorSpace::BT709
;
325 case gfx::ColorSpace2::BT2020
:
326 return gfx::YUVColorSpace::BT2020
;
328 MOZ_ASSERT_UNREACHABLE();
331 const bool yFlip
= destOrigin
!= srcOrigin
;
332 const DrawBlitProg::BaseArgs baseArgs
= {SubRectMat3(clipRect
, ySize
), yFlip
,
333 destSize
, Nothing()};
334 const DrawBlitProg::YUVArgs yuvArgs
= {
335 SubRectMat3(clipRect
, uvSize
, divisors
), Some(yuvColorSpace
)};
337 const auto& prog
= GetDrawBlitProg(
338 {kFragHeader_TexExt
, {kFragSample_TwoPlane
, kFragConvert_ColorMatrix
}});
339 prog
->Draw(baseArgs
, &yuvArgs
);
343 bool GLBlitHelper::BlitDescriptor(
344 const layers::SurfaceDescriptorDXGIYCbCr
& desc
,
345 const gfx::IntSize
& destSize
, const OriginPos destOrigin
) const {
346 const auto& clipSize
= desc
.size();
347 const auto& ySize
= desc
.sizeY();
348 const auto& uvSize
= desc
.sizeCbCr();
349 const auto& colorSpace
= desc
.yUVColorSpace();
351 const gfx::IntRect
clipRect(0, 0, clipSize
.width
, clipSize
.height
);
353 auto handleY
= desc
.handleY() ? desc
.handleY()->GetHandle() : nullptr;
354 auto handleCb
= desc
.handleCb() ? desc
.handleCb()->GetHandle() : nullptr;
355 auto handleCr
= desc
.handleCr() ? desc
.handleCr()->GetHandle() : nullptr;
357 const WindowsHandle handles
[3] = {
358 (WindowsHandle
)handleY
, (WindowsHandle
)handleCb
, (WindowsHandle
)handleCr
};
359 return BlitAngleYCbCr(handles
, clipRect
, ySize
, uvSize
, colorSpace
, destSize
,
365 bool GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList
)[3],
366 const gfx::IntRect
& clipRect
,
367 const gfx::IntSize
& ySize
,
368 const gfx::IntSize
& uvSize
,
369 const gfx::YUVColorSpace colorSpace
,
370 const gfx::IntSize
& destSize
,
371 const OriginPos destOrigin
) const {
372 const auto& d3d
= GetD3D11();
373 if (!d3d
) return false;
375 const auto srcOrigin
= OriginPos::BottomLeft
;
377 gfx::IntSize divisors
;
378 if (!GuessDivisors(ySize
, uvSize
, &divisors
)) return false;
380 const RefPtr
<ID3D11Texture2D
> texList
[3] = {
381 OpenSharedTexture(d3d
, handleList
[0]),
382 OpenSharedTexture(d3d
, handleList
[1]),
383 OpenSharedTexture(d3d
, handleList
[2])};
384 const BindAnglePlanes
bindPlanes(this, 3, texList
);
386 const bool yFlip
= destOrigin
!= srcOrigin
;
387 const DrawBlitProg::BaseArgs baseArgs
= {SubRectMat3(clipRect
, ySize
), yFlip
,
388 destSize
, Nothing()};
389 const DrawBlitProg::YUVArgs yuvArgs
= {
390 SubRectMat3(clipRect
, uvSize
, divisors
), Some(colorSpace
)};
392 const auto& prog
= GetDrawBlitProg(
393 {kFragHeader_TexExt
, {kFragSample_ThreePlane
, kFragConvert_ColorMatrix
}});
394 prog
->Draw(baseArgs
, &yuvArgs
);
399 } // namespace mozilla