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 "DCLayerTree.h"
11 #include "mozilla/gfx/AllOfDcomp.h"
18 #include "gfxWindowsPlatform.h"
19 #include "GLContext.h"
20 #include "GLContextEGL.h"
21 #include "mozilla/gfx/DeviceManagerDx.h"
22 #include "mozilla/gfx/Logging.h"
23 #include "mozilla/gfx/gfxVars.h"
24 #include "mozilla/gfx/GPUParent.h"
25 #include "mozilla/gfx/Matrix.h"
26 #include "mozilla/StaticPrefs_gfx.h"
27 #include "mozilla/webrender/RenderD3D11TextureHost.h"
28 #include "mozilla/webrender/RenderDcompSurfaceTextureHost.h"
29 #include "mozilla/webrender/RenderTextureHost.h"
30 #include "mozilla/webrender/RenderThread.h"
31 #include "mozilla/WindowsVersion.h"
32 #include "mozilla/Telemetry.h"
33 #include "nsPrintfCString.h"
38 #if defined(__MINGW32__) // 64 defines both 32 and 64
39 // We need to fake some things, while we wait on updates to mingw's dcomp.h
40 // header. Just enough that we can successfully fail to work there.
41 #define MOZ_MINGW_DCOMP_H_OLD
42 struct IDCompositionFilterEffect
: public IDCompositionEffect
{};
43 struct IDCompositionColorMatrixEffect
: public IDCompositionFilterEffect
{};
44 struct IDCompositionTableTransferEffect
: public IDCompositionFilterEffect
{};
45 struct IDCompositionDevice3
: public IDCompositionDevice2
{};
46 #endif // defined(__MINGW32__)
51 extern LazyLogModule gRenderThreadLog
;
52 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
54 #define LOG_H(msg, ...) \
55 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
56 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
58 UniquePtr
<GpuOverlayInfo
> DCLayerTree::sGpuOverlayInfo
;
61 UniquePtr
<DCLayerTree
> DCLayerTree::Create(gl::GLContext
* aGL
,
63 ID3D11Device
* aDevice
,
64 ID3D11DeviceContext
* aCtx
,
65 HWND aHwnd
, nsACString
& aError
) {
66 RefPtr
<IDCompositionDevice2
> dCompDevice
=
67 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
69 aError
.Assign("DCLayerTree(no device)"_ns
);
74 MakeUnique
<DCLayerTree
>(aGL
, aEGLConfig
, aDevice
, aCtx
, dCompDevice
);
75 if (!layerTree
->Initialize(aHwnd
, aError
)) {
82 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo
= nullptr; }
84 DCLayerTree::DCLayerTree(gl::GLContext
* aGL
, EGLConfig aEGLConfig
,
85 ID3D11Device
* aDevice
, ID3D11DeviceContext
* aCtx
,
86 IDCompositionDevice2
* aCompositionDevice
)
88 mEGLConfig(aEGLConfig
),
91 mCompositionDevice(aCompositionDevice
),
93 mDebugVisualRedrawRegions(false),
94 mEGLImage(EGL_NO_IMAGE
),
96 mPendingCommit(false) {
97 LOG("DCLayerTree::DCLayerTree()");
100 DCLayerTree::~DCLayerTree() {
101 LOG("DCLayerTree::~DCLayerTree()");
103 ReleaseNativeCompositorResources();
106 void DCLayerTree::ReleaseNativeCompositorResources() {
107 const auto gl
= GetGLContext();
111 // Delete any cached FBO objects
112 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
113 gl
->fDeleteRenderbuffers(1, &it
->depthRboId
);
114 gl
->fDeleteFramebuffers(1, &it
->fboId
);
118 bool DCLayerTree::Initialize(HWND aHwnd
, nsACString
& aError
) {
121 RefPtr
<IDCompositionDesktopDevice
> desktopDevice
;
122 hr
= mCompositionDevice
->QueryInterface(
123 (IDCompositionDesktopDevice
**)getter_AddRefs(desktopDevice
));
125 aError
.Assign(nsPrintfCString(
126 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr
));
130 hr
= desktopDevice
->CreateTargetForHwnd(aHwnd
, TRUE
,
131 getter_AddRefs(mCompositionTarget
));
133 aError
.Assign(nsPrintfCString(
134 "DCLayerTree(create DCompositionTarget failed %lx)", hr
));
138 hr
= mCompositionDevice
->CreateVisual(getter_AddRefs(mRootVisual
));
140 aError
.Assign(nsPrintfCString(
141 "DCLayerTree(create root DCompositionVisual failed %lx)", hr
));
146 mCompositionDevice
->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual
));
148 aError
.Assign(nsPrintfCString(
149 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr
));
153 if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) {
154 if (!InitializeVideoOverlaySupport()) {
155 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY
);
158 if (!sGpuOverlayInfo
) {
159 // Set default if sGpuOverlayInfo was not set.
160 sGpuOverlayInfo
= MakeUnique
<GpuOverlayInfo
>();
163 // Initialize SwapChainInfo
164 SupportsSwapChainTearing();
166 mCompositionTarget
->SetRoot(mRootVisual
);
167 // Set interporation mode to nearest, to ensure 1:1 sampling.
168 // By default, a visual inherits the interpolation mode of the parent visual.
169 // If no visuals set the interpolation mode, the default for the entire visual
170 // tree is nearest neighbor interpolation.
171 mRootVisual
->SetBitmapInterpolationMode(
172 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
176 bool FlagsSupportsOverlays(UINT flags
) {
177 return (flags
& (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
|
178 DXGI_OVERLAY_SUPPORT_FLAG_SCALING
));
181 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
182 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat
,
183 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace
,
184 RefPtr
<IDXGIOutput
> aOutput
,
185 RefPtr
<ID3D11Device
> aD3d11Device
) {
186 UINT colorSpaceSupportFlags
= 0;
187 RefPtr
<IDXGIOutput4
> output4
;
189 if (FAILED(aOutput
->QueryInterface(__uuidof(IDXGIOutput4
),
190 getter_AddRefs(output4
)))) {
194 if (FAILED(output4
->CheckOverlayColorSpaceSupport(
195 aDxgiFormat
, aDxgiColorSpace
, aD3d11Device
,
196 &colorSpaceSupportFlags
))) {
200 return (colorSpaceSupportFlags
&
201 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT
);
204 bool DCLayerTree::InitializeVideoOverlaySupport() {
205 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
209 hr
= mDevice
->QueryInterface(
210 (ID3D11VideoDevice
**)getter_AddRefs(mVideoDevice
));
212 gfxCriticalNote
<< "Failed to get D3D11VideoDevice: " << gfx::hexa(hr
);
217 mCtx
->QueryInterface((ID3D11VideoContext
**)getter_AddRefs(mVideoContext
));
219 gfxCriticalNote
<< "Failed to get D3D11VideoContext: " << gfx::hexa(hr
);
223 if (sGpuOverlayInfo
) {
227 UniquePtr
<GpuOverlayInfo
> info
= MakeUnique
<GpuOverlayInfo
>();
229 RefPtr
<IDXGIDevice
> dxgiDevice
;
230 RefPtr
<IDXGIAdapter
> adapter
;
231 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
232 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
236 RefPtr
<IDXGIOutput
> output
;
237 if (FAILED(adapter
->EnumOutputs(i
++, getter_AddRefs(output
)))) {
240 RefPtr
<IDXGIOutput3
> output3
;
241 if (FAILED(output
->QueryInterface(__uuidof(IDXGIOutput3
),
242 getter_AddRefs(output3
)))) {
246 output3
->CheckOverlaySupport(DXGI_FORMAT_NV12
, mDevice
,
247 &info
->mNv12OverlaySupportFlags
);
248 output3
->CheckOverlaySupport(DXGI_FORMAT_YUY2
, mDevice
,
249 &info
->mYuy2OverlaySupportFlags
);
250 output3
->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM
, mDevice
,
251 &info
->mBgra8OverlaySupportFlags
);
252 output3
->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM
, mDevice
,
253 &info
->mRgb10a2OverlaySupportFlags
);
255 if (FlagsSupportsOverlays(info
->mNv12OverlaySupportFlags
)) {
256 // NV12 format is preferred if it's supported.
257 info
->mOverlayFormatUsed
= DXGI_FORMAT_NV12
;
258 info
->mSupportsHardwareOverlays
= true;
261 if (!info
->mSupportsHardwareOverlays
&&
262 FlagsSupportsOverlays(info
->mYuy2OverlaySupportFlags
)) {
263 // If NV12 isn't supported, fallback to YUY2 if it's supported.
264 info
->mOverlayFormatUsed
= DXGI_FORMAT_YUY2
;
265 info
->mSupportsHardwareOverlays
= true;
268 // RGB10A2 overlay is used for displaying HDR content. In Intel's
269 // platform, RGB10A2 overlay is enabled only when
270 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
271 if (FlagsSupportsOverlays(info
->mRgb10a2OverlaySupportFlags
)) {
272 if (!CheckOverlayColorSpaceSupport(
273 DXGI_FORMAT_R10G10B10A2_UNORM
,
274 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
, output
, mDevice
))
275 info
->mRgb10a2OverlaySupportFlags
= 0;
278 // Early out after the first output that reports overlay support. All
279 // outputs are expected to report the same overlay support according to
280 // Microsoft's WDDM documentation:
281 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
282 if (info
->mSupportsHardwareOverlays
) {
287 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
288 info
->mOverlayFormatUsed
= DXGI_FORMAT_B8G8R8A8_UNORM
;
289 info
->mSupportsHardwareOverlays
= false;
292 info
->mSupportsOverlays
= info
->mSupportsHardwareOverlays
;
294 sGpuOverlayInfo
= std::move(info
);
296 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
297 gpuParent
->NotifyOverlayInfo(GetOverlayInfo());
303 DCSurface
* DCLayerTree::GetSurface(wr::NativeSurfaceId aId
) const {
304 auto surface_it
= mDCSurfaces
.find(aId
);
305 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
306 return surface_it
->second
.get();
309 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1
* aSwapChain
) {
310 LOG("DCLayerTree::SetDefaultSwapChain()");
312 mRootVisual
->AddVisual(mDefaultSwapChainVisual
, TRUE
, nullptr);
313 mDefaultSwapChainVisual
->SetContent(aSwapChain
);
314 // Default SwapChain's visual does not need linear interporation.
315 mDefaultSwapChainVisual
->SetBitmapInterpolationMode(
316 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
317 mPendingCommit
= true;
320 void DCLayerTree::MaybeUpdateDebug() {
321 bool updated
= false;
322 updated
|= MaybeUpdateDebugCounter();
323 updated
|= MaybeUpdateDebugVisualRedrawRegions();
325 mPendingCommit
= true;
329 void DCLayerTree::MaybeCommit() {
330 if (!mPendingCommit
) {
333 mCompositionDevice
->Commit();
336 void DCLayerTree::WaitForCommitCompletion() {
337 mCompositionDevice
->WaitForCommitCompletion();
340 void DCLayerTree::DisableNativeCompositor() {
341 MOZ_ASSERT(mCurrentSurface
.isNothing());
342 MOZ_ASSERT(mCurrentLayers
.empty());
344 ReleaseNativeCompositorResources();
346 mRootVisual
->RemoveAllVisuals();
349 bool DCLayerTree::MaybeUpdateDebugCounter() {
350 bool debugCounter
= StaticPrefs::gfx_webrender_debug_dcomp_counter();
351 if (mDebugCounter
== debugCounter
) {
355 RefPtr
<IDCompositionDeviceDebug
> debugDevice
;
356 HRESULT hr
= mCompositionDevice
->QueryInterface(
357 (IDCompositionDeviceDebug
**)getter_AddRefs(debugDevice
));
363 debugDevice
->EnableDebugCounters();
365 debugDevice
->DisableDebugCounters();
368 mDebugCounter
= debugCounter
;
372 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
373 bool debugVisualRedrawRegions
=
374 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
375 if (mDebugVisualRedrawRegions
== debugVisualRedrawRegions
) {
379 RefPtr
<IDCompositionVisualDebug
> visualDebug
;
380 HRESULT hr
= mRootVisual
->QueryInterface(
381 (IDCompositionVisualDebug
**)getter_AddRefs(visualDebug
));
386 if (debugVisualRedrawRegions
) {
387 visualDebug
->EnableRedrawRegions();
389 visualDebug
->DisableRedrawRegions();
392 mDebugVisualRedrawRegions
= debugVisualRedrawRegions
;
396 void DCLayerTree::CompositorBeginFrame() { mCurrentFrame
++; }
398 void DCLayerTree::CompositorEndFrame() {
399 auto start
= TimeStamp::Now();
400 // Check if the visual tree of surfaces is the same as last frame.
401 bool same
= mPrevLayers
== mCurrentLayers
;
404 // If not, we need to rebuild the visual tree. Note that addition or
405 // removal of tiles no longer needs to rebuild the main visual tree
406 // here, since they are added as children of the surface visual.
407 mRootVisual
->RemoveAllVisuals();
410 for (auto it
= mCurrentLayers
.begin(); it
!= mCurrentLayers
.end(); ++it
) {
411 auto surface_it
= mDCSurfaces
.find(*it
);
412 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
413 const auto surface
= surface_it
->second
.get();
414 // Ensure surface is trimmed to updated tile valid rects
415 surface
->UpdateAllocatedRect();
417 // Add surfaces in z-order they were added to the scene.
418 const auto visual
= surface
->GetVisual();
419 mRootVisual
->AddVisual(visual
, false, nullptr);
423 mPrevLayers
.swap(mCurrentLayers
);
424 mCurrentLayers
.clear();
426 mCompositionDevice
->Commit();
428 auto end
= TimeStamp::Now();
429 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
430 (end
- start
).ToMilliseconds() * 10.);
432 // Remove any framebuffers that haven't been
433 // used in the last 60 frames.
435 // This should use nsTArray::RemoveElementsBy once
436 // CachedFrameBuffer is able to properly destroy
437 // itself in the destructor.
438 const auto gl
= GetGLContext();
439 for (uint32_t i
= 0, len
= mFrameBuffers
.Length(); i
< len
; ++i
) {
440 auto& fb
= mFrameBuffers
[i
];
441 if ((mCurrentFrame
- fb
.lastFrameUsed
) > 60) {
442 gl
->fDeleteRenderbuffers(1, &fb
.depthRboId
);
443 gl
->fDeleteFramebuffers(1, &fb
.fboId
);
444 mFrameBuffers
.UnorderedRemoveElementAt(i
);
445 --i
; // Examine the element again, if necessary.
451 void DCLayerTree::Bind(wr::NativeTileId aId
, wr::DeviceIntPoint
* aOffset
,
452 uint32_t* aFboId
, wr::DeviceIntRect aDirtyRect
,
453 wr::DeviceIntRect aValidRect
) {
454 auto surface
= GetSurface(aId
.surface_id
);
455 auto tile
= surface
->GetTile(aId
.x
, aId
.y
);
456 wr::DeviceIntPoint targetOffset
{0, 0};
458 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
459 // IDCompositionVirtualSurface owned by the DCSurface.
460 RefPtr
<IDCompositionSurface
> compositionSurface
;
461 if (surface
->mIsVirtualSurface
) {
462 gfx::IntRect
validRect(aValidRect
.min
.x
, aValidRect
.min
.y
,
463 aValidRect
.width(), aValidRect
.height());
464 if (!tile
->mValidRect
.IsEqualEdges(validRect
)) {
465 tile
->mValidRect
= validRect
;
466 surface
->DirtyAllocatedRect();
468 wr::DeviceIntSize tileSize
= surface
->GetTileSize();
469 compositionSurface
= surface
->GetCompositionSurface();
470 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
471 targetOffset
.x
= virtualOffset
.x
+ tileSize
.width
* aId
.x
;
472 targetOffset
.y
= virtualOffset
.y
+ tileSize
.height
* aId
.y
;
474 compositionSurface
= tile
->Bind(aValidRect
);
477 if (tile
->mNeedsFullDraw
) {
478 // dcomp requires that the first BeginDraw on a non-virtual surface is the
479 // full size of the pixel buffer.
480 auto tileSize
= surface
->GetTileSize();
481 aDirtyRect
.min
.x
= 0;
482 aDirtyRect
.min
.y
= 0;
483 aDirtyRect
.max
.x
= tileSize
.width
;
484 aDirtyRect
.max
.y
= tileSize
.height
;
485 tile
->mNeedsFullDraw
= false;
488 *aFboId
= CreateEGLSurfaceForCompositionSurface(
489 aDirtyRect
, aOffset
, compositionSurface
, targetOffset
);
490 mCurrentSurface
= Some(compositionSurface
);
493 void DCLayerTree::Unbind() {
494 if (mCurrentSurface
.isNothing()) {
498 RefPtr
<IDCompositionSurface
> surface
= mCurrentSurface
.ref();
502 mCurrentSurface
= Nothing();
505 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId
,
506 wr::DeviceIntPoint aVirtualOffset
,
507 wr::DeviceIntSize aTileSize
, bool aIsOpaque
) {
508 auto it
= mDCSurfaces
.find(aId
);
509 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
510 if (it
!= mDCSurfaces
.end()) {
511 // DCSurface already exists.
515 // Tile size needs to be positive.
516 if (aTileSize
.width
<= 0 || aTileSize
.height
<= 0) {
517 gfxCriticalNote
<< "TileSize is not positive aId: " << wr::AsUint64(aId
)
518 << " aTileSize(" << aTileSize
.width
<< ","
519 << aTileSize
.height
<< ")";
522 bool isVirtualSurface
=
523 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
524 auto surface
= MakeUnique
<DCSurface
>(aTileSize
, aVirtualOffset
,
525 isVirtualSurface
, aIsOpaque
, this);
526 if (!surface
->Initialize()) {
527 gfxCriticalNote
<< "Failed to initialize DCSurface: " << wr::AsUint64(aId
);
531 mDCSurfaces
[aId
] = std::move(surface
);
534 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId
,
536 auto it
= mDCSurfaces
.find(aId
);
537 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
539 auto surface
= MakeUnique
<DCExternalSurfaceWrapper
>(aIsOpaque
, this);
540 if (!surface
->Initialize()) {
541 gfxCriticalNote
<< "Failed to initialize DCExternalSurfaceWrapper: "
542 << wr::AsUint64(aId
);
546 mDCSurfaces
[aId
] = std::move(surface
);
549 void DCLayerTree::DestroySurface(NativeSurfaceId aId
) {
550 auto surface_it
= mDCSurfaces
.find(aId
);
551 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
552 auto surface
= surface_it
->second
.get();
554 mRootVisual
->RemoveVisual(surface
->GetVisual());
555 mDCSurfaces
.erase(surface_it
);
558 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
559 auto surface
= GetSurface(aId
);
560 surface
->CreateTile(aX
, aY
);
563 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
564 auto surface
= GetSurface(aId
);
565 surface
->DestroyTile(aX
, aY
);
568 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId
,
569 wr::ExternalImageId aExternalImage
) {
570 auto surface_it
= mDCSurfaces
.find(aId
);
571 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
572 surface_it
->second
->AttachExternalImage(aExternalImage
);
575 void DCExternalSurfaceWrapper::AttachExternalImage(
576 wr::ExternalImageId aExternalImage
) {
577 if (auto* surface
= EnsureSurfaceForExternalImage(aExternalImage
)) {
578 surface
->AttachExternalImage(aExternalImage
);
584 template <class FromT
>
585 [[nodiscard
]] static inline RefPtr
<ToT
> From(FromT
* const from
) {
587 (void)from
->QueryInterface(static_cast<ToT
**>(getter_AddRefs(to
)));
592 DCSurface
* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
593 wr::ExternalImageId aExternalImage
) {
595 return mSurface
.get();
598 // Create a new surface based on the texture type.
599 RenderTextureHost
* texture
=
600 RenderThread::Get()->GetRenderTexture(aExternalImage
);
601 if (texture
&& texture
->AsRenderDXGITextureHost()) {
602 mSurface
.reset(new DCSurfaceVideo(mIsOpaque
, mDCLayerTree
));
603 if (!mSurface
->Initialize()) {
604 gfxCriticalNote
<< "Failed to initialize DCSurfaceVideo: "
605 << wr::AsUint64(aExternalImage
);
608 } else if (texture
&& texture
->AsRenderDcompSurfaceTextureHost()) {
609 mSurface
.reset(new DCSurfaceHandle(mIsOpaque
, mDCLayerTree
));
610 if (!mSurface
->Initialize()) {
611 gfxCriticalNote
<< "Failed to initialize DCSurfaceHandle: "
612 << wr::AsUint64(aExternalImage
);
617 gfxCriticalNote
<< "Failed to create a surface for external image: "
618 << gfx::hexa(texture
);
621 const auto textureSwgl
= texture
->AsRenderTextureHostSWGL();
622 MOZ_ASSERT(textureSwgl
); // Covered above.
624 // Add surface's visual which will contain video data to our root visual.
625 const auto surfaceVisual
= mSurface
->GetVisual();
626 mVisual
->AddVisual(surfaceVisual
, true, nullptr);
629 // Apply color management.
632 const auto cmsMode
= GfxColorManagementMode();
633 if (cmsMode
== CMSMode::Off
) return;
635 const auto dcomp
= mDCLayerTree
->GetCompositionDevice();
636 RefPtr
<IDCompositionDevice3
> dcomp3
;
637 #if !defined(MOZ_MINGW_DCOMP_H_OLD)
638 dcomp3
= QI
<IDCompositionDevice3
>::From(dcomp
);
644 "No IDCompositionDevice3, cannot use dcomp for color management.");
650 const auto cspace
= [&]() {
651 const auto rangedCspace
= textureSwgl
->GetYUVColorSpace();
652 const auto info
= FromYUVRangedColorSpace(rangedCspace
);
653 auto ret
= ToColorSpace2(info
.space
);
654 if (ret
== gfx::ColorSpace2::Display
&& cmsMode
== CMSMode::All
) {
655 ret
= gfx::ColorSpace2::SRGB
;
660 const bool rec709GammaAsSrgb
=
661 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
662 const bool rec2020GammaAsRec709
=
663 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
665 auto cspaceDesc
= color::ColorspaceDesc
{};
667 case gfx::ColorSpace2::Display
:
668 return; // No color management needed!
669 case gfx::ColorSpace2::SRGB
:
670 cspaceDesc
.chrom
= color::Chromaticities::Srgb();
671 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
674 case gfx::ColorSpace2::DISPLAY_P3
:
675 cspaceDesc
.chrom
= color::Chromaticities::DisplayP3();
676 cspaceDesc
.tf
= color::PiecewiseGammaDesc::DisplayP3();
679 case gfx::ColorSpace2::BT601_525
:
680 cspaceDesc
.chrom
= color::Chromaticities::Rec601_525_Ntsc();
681 if (rec709GammaAsSrgb
) {
682 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
684 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
688 case gfx::ColorSpace2::BT709
:
689 cspaceDesc
.chrom
= color::Chromaticities::Rec709();
690 if (rec709GammaAsSrgb
) {
691 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
693 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
697 case gfx::ColorSpace2::BT2020
:
698 cspaceDesc
.chrom
= color::Chromaticities::Rec2020();
699 if (rec2020GammaAsRec709
&& rec709GammaAsSrgb
) {
700 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
701 } else if (rec2020GammaAsRec709
) {
702 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
704 // Just Rec709 with slightly more precision.
705 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec2020_12bit();
710 const auto cprofileIn
= color::ColorProfileDesc::From(cspaceDesc
);
711 auto cprofileOut
= mDCLayerTree
->OutputColorProfile();
712 bool pretendSrgb
= StaticPrefs::gfx_color_management_native_srgb();
714 cprofileOut
= color::ColorProfileDesc::From({
715 color::Chromaticities::Srgb(),
716 color::PiecewiseGammaDesc::Srgb(),
719 const auto conversion
= color::ColorProfileConversionDesc::From({
726 auto chain
= ColorManagementChain::From(*dcomp3
, conversion
);
727 mCManageChain
= Some(chain
);
729 surfaceVisual
->SetEffect(mCManageChain
->last
.get());
732 return mSurface
.get();
735 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix
& aTransform
) {
736 MOZ_ASSERT(mSurface
);
737 if (auto* surface
= mSurface
->AsDCSurfaceVideo()) {
738 if (surface
->CalculateSwapChainSize(aTransform
)) {
739 surface
->PresentVideo();
741 } else if (auto* surface
= mSurface
->AsDCSurfaceHandle()) {
742 surface
->PresentSurfaceHandle();
746 template <typename T
>
747 static inline D2D1_RECT_F
D2DRect(const T
& aRect
) {
748 return D2D1::RectF(aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost());
751 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const gfx::Matrix
& aTransform
) {
752 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
, aTransform
._21
,
753 aTransform
._22
, aTransform
._31
, aTransform
._32
);
756 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId
,
757 const wr::CompositorSurfaceTransform
& aTransform
,
758 wr::DeviceIntRect aClipRect
,
759 wr::ImageRendering aImageRendering
) {
760 auto it
= mDCSurfaces
.find(aId
);
761 MOZ_RELEASE_ASSERT(it
!= mDCSurfaces
.end());
762 const auto surface
= it
->second
.get();
763 const auto visual
= surface
->GetVisual();
765 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
767 float sx
= aTransform
.scale
.x
;
768 float sy
= aTransform
.scale
.y
;
769 float tx
= aTransform
.offset
.x
;
770 float ty
= aTransform
.offset
.y
;
771 gfx::Matrix
transform(sx
, 0.0, 0.0, sy
, tx
, ty
);
773 surface
->PresentExternalSurface(transform
);
775 transform
.PreTranslate(-virtualOffset
.x
, -virtualOffset
.y
);
777 // The DirectComposition API applies clipping *before* any
778 // transforms/offset, whereas we want the clip applied after. Right now, we
779 // only support rectilinear transforms, and then we transform our clip into
780 // pre-transform coordinate space for it to be applied there.
781 // DirectComposition does have an option for pre-transform clipping, if you
782 // create an explicit IDCompositionEffectGroup object and set a 3D transform
783 // on that. I suspect that will perform worse though, so we should only do
784 // that for complex transforms (which are never provided right now).
785 MOZ_ASSERT(transform
.IsRectilinear());
786 gfx::Rect clip
= transform
.Inverse().TransformBounds(gfx::Rect(
787 aClipRect
.min
.x
, aClipRect
.min
.y
, aClipRect
.width(), aClipRect
.height()));
788 // Set the clip rect - converting from world space to the pre-offset space
789 // that DC requires for rectangle clips.
790 visual
->SetClip(D2DRect(clip
));
792 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
793 // the D3D API level (unless we QI to IDCompositionVisual3, which might
794 // not be available?).
795 // Should we assert here, or restrict at the WR API level.
796 visual
->SetTransform(D2DMatrix(transform
));
798 if (aImageRendering
== wr::ImageRendering::Auto
) {
799 visual
->SetBitmapInterpolationMode(
800 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR
);
802 visual
->SetBitmapInterpolationMode(
803 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
806 mCurrentLayers
.push_back(aId
);
809 GLuint
DCLayerTree::GetOrCreateFbo(int aWidth
, int aHeight
) {
810 const auto gl
= GetGLContext();
813 // Check if we have a cached FBO with matching dimensions
814 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
815 if (it
->width
== aWidth
&& it
->height
== aHeight
) {
817 it
->lastFrameUsed
= mCurrentFrame
;
822 // If not, create a new FBO with attached depth buffer
824 // Create the depth buffer
826 gl
->fGenRenderbuffers(1, &depthRboId
);
827 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, depthRboId
);
828 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, LOCAL_GL_DEPTH_COMPONENT24
,
831 // Create the framebuffer and attach the depth buffer to it
832 gl
->fGenFramebuffers(1, &fboId
);
833 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
834 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
835 LOCAL_GL_DEPTH_ATTACHMENT
,
836 LOCAL_GL_RENDERBUFFER
, depthRboId
);
838 // Store this in the cache for future calls.
839 // TODO(gw): Maybe we should periodically scan this list and remove old
841 // haven't been used for some time?
842 DCLayerTree::CachedFrameBuffer frame_buffer_info
;
843 frame_buffer_info
.width
= aWidth
;
844 frame_buffer_info
.height
= aHeight
;
845 frame_buffer_info
.fboId
= fboId
;
846 frame_buffer_info
.depthRboId
= depthRboId
;
847 frame_buffer_info
.lastFrameUsed
= mCurrentFrame
;
848 mFrameBuffers
.AppendElement(frame_buffer_info
);
854 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize
& aInputSize
,
855 const gfx::IntSize
& aOutputSize
) {
858 if (!mVideoDevice
|| !mVideoContext
) {
862 if (mVideoProcessor
&& (aInputSize
<= mVideoInputSize
) &&
863 (aOutputSize
<= mVideoOutputSize
)) {
867 mVideoProcessor
= nullptr;
868 mVideoProcessorEnumerator
= nullptr;
870 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc
= {};
871 desc
.InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE
;
872 desc
.InputFrameRate
.Numerator
= 60;
873 desc
.InputFrameRate
.Denominator
= 1;
874 desc
.InputWidth
= aInputSize
.width
;
875 desc
.InputHeight
= aInputSize
.height
;
876 desc
.OutputFrameRate
.Numerator
= 60;
877 desc
.OutputFrameRate
.Denominator
= 1;
878 desc
.OutputWidth
= aOutputSize
.width
;
879 desc
.OutputHeight
= aOutputSize
.height
;
880 desc
.Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
;
882 hr
= mVideoDevice
->CreateVideoProcessorEnumerator(
883 &desc
, getter_AddRefs(mVideoProcessorEnumerator
));
885 gfxCriticalNote
<< "Failed to create VideoProcessorEnumerator: "
890 hr
= mVideoDevice
->CreateVideoProcessor(mVideoProcessorEnumerator
, 0,
891 getter_AddRefs(mVideoProcessor
));
893 mVideoProcessor
= nullptr;
894 mVideoProcessorEnumerator
= nullptr;
895 gfxCriticalNote
<< "Failed to create VideoProcessor: " << gfx::hexa(hr
);
899 // Reduce power cosumption
900 // By default, the driver might perform certain processing tasks
902 mVideoContext
->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor
, 0,
905 mVideoInputSize
= aInputSize
;
906 mVideoOutputSize
= aOutputSize
;
911 bool DCLayerTree::SupportsHardwareOverlays() {
912 return sGpuOverlayInfo
->mSupportsHardwareOverlays
;
915 bool DCLayerTree::SupportsSwapChainTearing() {
916 RefPtr
<ID3D11Device
> device
= mDevice
;
917 static const bool supported
= [device
] {
918 RefPtr
<IDXGIDevice
> dxgiDevice
;
919 RefPtr
<IDXGIAdapter
> adapter
;
920 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
921 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
923 RefPtr
<IDXGIFactory5
> dxgiFactory
;
924 HRESULT hr
= adapter
->GetParent(
925 IID_PPV_ARGS((IDXGIFactory5
**)getter_AddRefs(dxgiFactory
)));
930 BOOL presentAllowTearing
= FALSE
;
931 hr
= dxgiFactory
->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING
,
932 &presentAllowTearing
,
933 sizeof(presentAllowTearing
));
938 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
939 gpuParent
->NotifySwapChainInfo(
940 layers::SwapChainInfo(!!presentAllowTearing
));
941 } else if (XRE_IsParentProcess()) {
942 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
944 return !!presentAllowTearing
;
949 DXGI_FORMAT
DCLayerTree::GetOverlayFormatForSDR() {
950 return sGpuOverlayInfo
->mOverlayFormatUsed
;
953 static layers::OverlaySupportType
FlagsToOverlaySupportType(
954 UINT aFlags
, bool aSoftwareOverlaySupported
) {
955 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_SCALING
) {
956 return layers::OverlaySupportType::Scaling
;
958 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
) {
959 return layers::OverlaySupportType::Direct
;
961 if (aSoftwareOverlaySupported
) {
962 return layers::OverlaySupportType::Software
;
964 return layers::OverlaySupportType::None
;
967 layers::OverlayInfo
DCLayerTree::GetOverlayInfo() {
968 layers::OverlayInfo info
;
970 info
.mSupportsOverlays
= sGpuOverlayInfo
->mSupportsHardwareOverlays
;
972 FlagsToOverlaySupportType(sGpuOverlayInfo
->mNv12OverlaySupportFlags
,
973 /* aSoftwareOverlaySupported */ false);
975 FlagsToOverlaySupportType(sGpuOverlayInfo
->mYuy2OverlaySupportFlags
,
976 /* aSoftwareOverlaySupported */ false);
978 FlagsToOverlaySupportType(sGpuOverlayInfo
->mBgra8OverlaySupportFlags
,
979 /* aSoftwareOverlaySupported */ true);
980 info
.mRgb10a2Overlay
=
981 FlagsToOverlaySupportType(sGpuOverlayInfo
->mRgb10a2OverlaySupportFlags
,
982 /* aSoftwareOverlaySupported */ false);
987 DCSurface::DCSurface(wr::DeviceIntSize aTileSize
,
988 wr::DeviceIntPoint aVirtualOffset
, bool aIsVirtualSurface
,
989 bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
990 : mIsVirtualSurface(aIsVirtualSurface
),
991 mDCLayerTree(aDCLayerTree
),
992 mTileSize(aTileSize
),
993 mIsOpaque(aIsOpaque
),
994 mAllocatedRectDirty(true),
995 mVirtualOffset(aVirtualOffset
) {}
997 DCSurface::~DCSurface() {}
999 bool DCSurface::Initialize() {
1000 // Create a visual for tiles to attach to, whether virtual or not.
1002 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1003 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1005 gfxCriticalNote
<< "Failed to create DCompositionVisual: " << gfx::hexa(hr
);
1009 // If virtual surface is enabled, create and attach to visual, in this case
1010 // the tiles won't own visuals or surfaces.
1011 if (mIsVirtualSurface
) {
1012 DXGI_ALPHA_MODE alpha_mode
=
1013 mIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1015 hr
= dCompDevice
->CreateVirtualSurface(
1016 VIRTUAL_SURFACE_SIZE
, VIRTUAL_SURFACE_SIZE
, DXGI_FORMAT_R8G8B8A8_UNORM
,
1017 alpha_mode
, getter_AddRefs(mVirtualSurface
));
1018 MOZ_ASSERT(SUCCEEDED(hr
));
1020 // Bind the surface memory to this visual
1021 hr
= mVisual
->SetContent(mVirtualSurface
);
1022 MOZ_ASSERT(SUCCEEDED(hr
));
1028 void DCSurface::CreateTile(int32_t aX
, int32_t aY
) {
1029 TileKey
key(aX
, aY
);
1030 MOZ_RELEASE_ASSERT(mDCTiles
.find(key
) == mDCTiles
.end());
1032 auto tile
= MakeUnique
<DCTile
>(mDCLayerTree
);
1033 if (!tile
->Initialize(aX
, aY
, mTileSize
, mIsVirtualSurface
, mIsOpaque
,
1035 gfxCriticalNote
<< "Failed to initialize DCTile: " << aX
<< aY
;
1039 if (mIsVirtualSurface
) {
1040 mAllocatedRectDirty
= true;
1042 mVisual
->AddVisual(tile
->GetVisual(), false, nullptr);
1045 mDCTiles
[key
] = std::move(tile
);
1048 void DCSurface::DestroyTile(int32_t aX
, int32_t aY
) {
1049 TileKey
key(aX
, aY
);
1050 if (mIsVirtualSurface
) {
1051 mAllocatedRectDirty
= true;
1053 auto tile
= GetTile(aX
, aY
);
1054 mVisual
->RemoveVisual(tile
->GetVisual());
1056 mDCTiles
.erase(key
);
1059 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty
= true; }
1061 void DCSurface::UpdateAllocatedRect() {
1062 if (mAllocatedRectDirty
) {
1063 if (mVirtualSurface
) {
1064 // The virtual surface may have holes in it (for example, an empty tile
1065 // that has no primitives). Instead of trimming to a single bounding
1066 // rect, supply the rect of each valid tile to handle this case.
1067 std::vector
<RECT
> validRects
;
1069 for (auto it
= mDCTiles
.begin(); it
!= mDCTiles
.end(); ++it
) {
1070 auto tile
= GetTile(it
->first
.mX
, it
->first
.mY
);
1073 rect
.left
= (LONG
)(mVirtualOffset
.x
+ it
->first
.mX
* mTileSize
.width
+
1074 tile
->mValidRect
.x
);
1075 rect
.top
= (LONG
)(mVirtualOffset
.y
+ it
->first
.mY
* mTileSize
.height
+
1076 tile
->mValidRect
.y
);
1077 rect
.right
= rect
.left
+ tile
->mValidRect
.width
;
1078 rect
.bottom
= rect
.top
+ tile
->mValidRect
.height
;
1080 validRects
.push_back(rect
);
1083 mVirtualSurface
->Trim(validRects
.data(), validRects
.size());
1085 // When not using a virtual surface, we still want to reset this
1086 mAllocatedRectDirty
= false;
1090 DCTile
* DCSurface::GetTile(int32_t aX
, int32_t aY
) const {
1091 TileKey
key(aX
, aY
);
1092 auto tile_it
= mDCTiles
.find(key
);
1093 MOZ_RELEASE_ASSERT(tile_it
!= mDCTiles
.end());
1094 return tile_it
->second
.get();
1097 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1098 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1101 DCSurfaceVideo::~DCSurfaceVideo() {
1102 ReleaseDecodeSwapChainResources();
1103 MOZ_ASSERT(!mSwapChainSurfaceHandle
);
1106 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat
) {
1107 if (aFormat
== DXGI_FORMAT_NV12
|| aFormat
== DXGI_FORMAT_YUY2
) {
1113 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1114 RenderTextureHost
* texture
=
1115 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1116 MOZ_RELEASE_ASSERT(texture
);
1118 if (mPrevTexture
== texture
) {
1122 // XXX if software decoded video frame format is nv12, it could be used as
1124 if (!texture
|| !texture
->AsRenderDXGITextureHost() ||
1125 texture
->AsRenderDXGITextureHost()->GetFormat() !=
1126 gfx::SurfaceFormat::NV12
) {
1127 gfxCriticalNote
<< "Unsupported RenderTexture for overlay: "
1128 << gfx::hexa(texture
);
1132 mRenderTextureHost
= texture
;
1135 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix
& aTransform
) {
1136 if (!mRenderTextureHost
) {
1137 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1141 mVideoSize
= mRenderTextureHost
->AsRenderDXGITextureHost()->GetSize(0);
1143 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1144 // DCSurfaceVideo::PresentVideo() needs to be called.
1145 bool needsToPresent
= mPrevTexture
!= mRenderTextureHost
;
1146 gfx::IntSize swapChainSize
= mVideoSize
;
1147 gfx::Matrix transform
= aTransform
;
1149 // When video is rendered to axis aligned integer rectangle, video scaling
1150 // could be done by VideoProcessor
1151 bool scaleVideoAtVideoProcessor
= false;
1152 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1153 aTransform
.PreservesAxisAlignedRectangles()) {
1154 gfx::Size scaledSize
= gfx::Size(mVideoSize
) * aTransform
.ScaleFactors();
1155 gfx::IntSize
size(int32_t(std::round(scaledSize
.width
)),
1156 int32_t(std::round(scaledSize
.height
)));
1157 if (gfx::FuzzyEqual(scaledSize
.width
, size
.width
, 0.1f
) &&
1158 gfx::FuzzyEqual(scaledSize
.height
, size
.height
, 0.1f
)) {
1159 scaleVideoAtVideoProcessor
= true;
1160 swapChainSize
= size
;
1164 if (scaleVideoAtVideoProcessor
) {
1165 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1166 // subsampled formats like NV12 must have an even width and height.
1167 if (swapChainSize
.width
% 2 == 1) {
1168 swapChainSize
.width
+= 1;
1170 if (swapChainSize
.height
% 2 == 1) {
1171 swapChainSize
.height
+= 1;
1173 transform
= gfx::Matrix::Translation(aTransform
.GetTranslation());
1176 if (!mVideoSwapChain
|| mSwapChainSize
!= swapChainSize
) {
1177 needsToPresent
= true;
1178 ReleaseDecodeSwapChainResources();
1179 // Update mSwapChainSize before creating SwapChain
1180 mSwapChainSize
= swapChainSize
;
1182 auto swapChainFormat
= GetSwapChainFormat();
1183 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1184 if (useYUVSwapChain
) {
1185 // Tries to create YUV SwapChain
1186 CreateVideoSwapChain();
1187 if (!mVideoSwapChain
) {
1188 mFailedYuvSwapChain
= true;
1189 ReleaseDecodeSwapChainResources();
1191 gfxCriticalNote
<< "Fallback to RGB SwapChain";
1194 // Tries to create RGB SwapChain
1195 if (!mVideoSwapChain
) {
1196 CreateVideoSwapChain();
1200 aTransform
= transform
;
1202 return needsToPresent
;
1205 void DCSurfaceVideo::PresentVideo() {
1206 if (!mRenderTextureHost
) {
1210 if (!mVideoSwapChain
) {
1211 gfxCriticalNote
<< "Failed to create VideoSwapChain";
1212 RenderThread::Get()->NotifyWebRenderError(
1213 wr::WebRenderError::VIDEO_OVERLAY
);
1217 mVisual
->SetContent(mVideoSwapChain
);
1219 if (!CallVideoProcessorBlt()) {
1220 auto swapChainFormat
= GetSwapChainFormat();
1221 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1222 if (useYUVSwapChain
) {
1223 mFailedYuvSwapChain
= true;
1224 ReleaseDecodeSwapChainResources();
1227 RenderThread::Get()->NotifyWebRenderError(
1228 wr::WebRenderError::VIDEO_OVERLAY
);
1233 hr
= mVideoSwapChain
->Present(0, 0);
1234 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1235 gfxCriticalNoteOnce
<< "video Present failed: " << gfx::hexa(hr
);
1238 mPrevTexture
= mRenderTextureHost
;
1241 DXGI_FORMAT
DCSurfaceVideo::GetSwapChainFormat() {
1242 if (mFailedYuvSwapChain
|| !mDCLayerTree
->SupportsHardwareOverlays()) {
1243 return DXGI_FORMAT_B8G8R8A8_UNORM
;
1245 return mDCLayerTree
->GetOverlayFormatForSDR();
1248 bool DCSurfaceVideo::CreateVideoSwapChain() {
1249 MOZ_ASSERT(mRenderTextureHost
);
1251 const auto device
= mDCLayerTree
->GetDevice();
1253 RefPtr
<IDXGIDevice
> dxgiDevice
;
1254 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1256 RefPtr
<IDXGIFactoryMedia
> dxgiFactoryMedia
;
1258 RefPtr
<IDXGIAdapter
> adapter
;
1259 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1261 IID_PPV_ARGS((IDXGIFactoryMedia
**)getter_AddRefs(dxgiFactoryMedia
)));
1264 mSwapChainSurfaceHandle
= gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1265 if (!mSwapChainSurfaceHandle
) {
1266 gfxCriticalNote
<< "Failed to create DCompSurfaceHandle";
1270 auto swapChainFormat
= GetSwapChainFormat();
1272 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1273 desc
.Width
= mSwapChainSize
.width
;
1274 desc
.Height
= mSwapChainSize
.height
;
1275 desc
.Format
= swapChainFormat
;
1276 desc
.Stereo
= FALSE
;
1277 desc
.SampleDesc
.Count
= 1;
1278 desc
.BufferCount
= 2;
1279 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1280 desc
.Scaling
= DXGI_SCALING_STRETCH
;
1281 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1282 desc
.Flags
= DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO
;
1283 if (IsYUVSwapChainFormat(swapChainFormat
)) {
1284 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO
;
1286 desc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
1289 hr
= dxgiFactoryMedia
->CreateSwapChainForCompositionSurfaceHandle(
1290 device
, mSwapChainSurfaceHandle
, &desc
, nullptr,
1291 getter_AddRefs(mVideoSwapChain
));
1294 gfxCriticalNote
<< "Failed to create video SwapChain: " << gfx::hexa(hr
)
1295 << " " << mSwapChainSize
;
1299 mSwapChainFormat
= swapChainFormat
;
1303 // TODO: Replace with YUVRangedColorSpace
1304 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1305 const gfx::YUVColorSpace aYUVColorSpace
,
1306 const gfx::ColorRange aColorRange
) {
1307 if (aYUVColorSpace
== gfx::YUVColorSpace::BT601
) {
1308 if (aColorRange
== gfx::ColorRange::FULL
) {
1309 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601
);
1311 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
);
1313 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT709
) {
1314 if (aColorRange
== gfx::ColorRange::FULL
) {
1315 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
);
1317 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
);
1319 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT2020
) {
1320 if (aColorRange
== gfx::ColorRange::FULL
) {
1321 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1323 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020
);
1325 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
);
1332 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1333 const gfx::YUVRangedColorSpace aYUVColorSpace
) {
1334 const auto info
= FromYUVRangedColorSpace(aYUVColorSpace
);
1335 return GetSourceDXGIColorSpace(info
.space
, info
.range
);
1338 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1339 MOZ_ASSERT(mRenderTextureHost
);
1342 const auto videoDevice
= mDCLayerTree
->GetVideoDevice();
1343 const auto videoContext
= mDCLayerTree
->GetVideoContext();
1344 const auto texture
= mRenderTextureHost
->AsRenderDXGITextureHost();
1346 Maybe
<DXGI_COLOR_SPACE_TYPE
> sourceColorSpace
=
1347 GetSourceDXGIColorSpace(texture
->GetYUVColorSpace());
1348 if (sourceColorSpace
.isNothing()) {
1349 gfxCriticalNote
<< "Unsupported color space";
1353 RefPtr
<ID3D11Texture2D
> texture2D
= texture
->GetD3D11Texture2DWithGL();
1355 gfxCriticalNote
<< "Failed to get D3D11Texture2D";
1359 if (!mVideoSwapChain
) {
1363 if (!mDCLayerTree
->EnsureVideoProcessor(mVideoSize
, mSwapChainSize
)) {
1364 gfxCriticalNote
<< "EnsureVideoProcessor Failed";
1368 RefPtr
<IDXGISwapChain3
> swapChain3
;
1369 mVideoSwapChain
->QueryInterface(
1370 (IDXGISwapChain3
**)getter_AddRefs(swapChain3
));
1372 gfxCriticalNote
<< "Failed to get IDXGISwapChain3";
1376 RefPtr
<ID3D11VideoContext1
> videoContext1
;
1377 videoContext
->QueryInterface(
1378 (ID3D11VideoContext1
**)getter_AddRefs(videoContext1
));
1379 if (!videoContext1
) {
1380 gfxCriticalNote
<< "Failed to get ID3D11VideoContext1";
1384 const auto videoProcessor
= mDCLayerTree
->GetVideoProcessor();
1385 const auto videoProcessorEnumerator
=
1386 mDCLayerTree
->GetVideoProcessorEnumerator();
1388 DXGI_COLOR_SPACE_TYPE inputColorSpace
= sourceColorSpace
.ref();
1389 videoContext1
->VideoProcessorSetStreamColorSpace1(videoProcessor
, 0,
1392 DXGI_COLOR_SPACE_TYPE outputColorSpace
=
1393 IsYUVSwapChainFormat(mSwapChainFormat
)
1395 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1396 hr
= swapChain3
->SetColorSpace1(outputColorSpace
);
1398 gfxCriticalNoteOnce
<< "SetColorSpace1 failed: " << gfx::hexa(hr
);
1399 RenderThread::Get()->NotifyWebRenderError(
1400 wr::WebRenderError::VIDEO_OVERLAY
);
1403 videoContext1
->VideoProcessorSetOutputColorSpace1(videoProcessor
,
1406 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc
= {};
1407 inputDesc
.ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
;
1408 inputDesc
.Texture2D
.ArraySlice
= texture
->ArrayIndex();
1410 RefPtr
<ID3D11VideoProcessorInputView
> inputView
;
1411 hr
= videoDevice
->CreateVideoProcessorInputView(
1412 texture2D
, videoProcessorEnumerator
, &inputDesc
,
1413 getter_AddRefs(inputView
));
1415 gfxCriticalNote
<< "ID3D11VideoProcessorInputView creation failed: "
1420 D3D11_VIDEO_PROCESSOR_STREAM stream
= {};
1421 stream
.Enable
= true;
1422 stream
.OutputIndex
= 0;
1423 stream
.InputFrameOrField
= 0;
1424 stream
.PastFrames
= 0;
1425 stream
.FutureFrames
= 0;
1426 stream
.pInputSurface
= inputView
.get();
1431 destRect
.right
= mSwapChainSize
.width
;
1432 destRect
.bottom
= mSwapChainSize
.height
;
1434 videoContext
->VideoProcessorSetOutputTargetRect(videoProcessor
, TRUE
,
1436 videoContext
->VideoProcessorSetStreamDestRect(videoProcessor
, 0, TRUE
,
1439 sourceRect
.left
= 0;
1441 sourceRect
.right
= mVideoSize
.width
;
1442 sourceRect
.bottom
= mVideoSize
.height
;
1443 videoContext
->VideoProcessorSetStreamSourceRect(videoProcessor
, 0, TRUE
,
1447 RefPtr
<ID3D11Texture2D
> backBuf
;
1448 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1449 (void**)getter_AddRefs(backBuf
));
1451 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc
= {};
1452 outputDesc
.ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2D
;
1453 outputDesc
.Texture2D
.MipSlice
= 0;
1455 hr
= videoDevice
->CreateVideoProcessorOutputView(
1456 backBuf
, videoProcessorEnumerator
, &outputDesc
,
1457 getter_AddRefs(mOutputView
));
1459 gfxCriticalNote
<< "ID3D11VideoProcessorOutputView creation failed: "
1465 hr
= videoContext
->VideoProcessorBlt(videoProcessor
, mOutputView
, 0, 1,
1468 gfxCriticalNote
<< "VideoProcessorBlt failed: " << gfx::hexa(hr
);
1475 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1476 mOutputView
= nullptr;
1477 mVideoSwapChain
= nullptr;
1478 mDecodeSwapChain
= nullptr;
1479 mDecodeResource
= nullptr;
1480 if (mSwapChainSurfaceHandle
) {
1481 ::CloseHandle(mSwapChainSurfaceHandle
);
1482 mSwapChainSurfaceHandle
= 0;
1486 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1487 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1490 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1491 RenderTextureHost
* texture
=
1492 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1493 RenderDcompSurfaceTextureHost
* renderTexture
=
1494 texture
? texture
->AsRenderDcompSurfaceTextureHost() : nullptr;
1495 if (!renderTexture
) {
1496 gfxCriticalNote
<< "Unsupported RenderTexture for DCSurfaceHandle: "
1497 << gfx::hexa(texture
);
1501 const auto handle
= renderTexture
->GetDcompSurfaceHandle();
1502 if (GetSurfaceHandle() == handle
) {
1506 LOG_H("AttachExternalImage, ext-image=%" PRIu64
", texture=%p, handle=%p",
1507 wr::AsUint64(aExternalImage
), renderTexture
, handle
);
1508 mDcompTextureHost
= renderTexture
;
1511 HANDLE
DCSurfaceHandle::GetSurfaceHandle() const {
1512 if (mDcompTextureHost
) {
1513 return mDcompTextureHost
->GetDcompSurfaceHandle();
1518 IDCompositionSurface
* DCSurfaceHandle::EnsureSurface() {
1519 if (auto* surface
= mDcompTextureHost
->GetSurface()) {
1523 // Texture host hasn't created the surface yet, ask it to create a new one.
1524 RefPtr
<IDCompositionDevice
> device
;
1525 HRESULT hr
= mDCLayerTree
->GetCompositionDevice()->QueryInterface(
1526 (IDCompositionDevice
**)getter_AddRefs(device
));
1529 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1534 return mDcompTextureHost
->CreateSurfaceFromDevice(device
);
1537 void DCSurfaceHandle::PresentSurfaceHandle() {
1538 LOG_H("PresentSurfaceHandle");
1539 if (IDCompositionSurface
* surface
= EnsureSurface()) {
1540 LOG_H("Set surface %p to visual", surface
);
1541 mVisual
->SetContent(surface
);
1543 mVisual
->SetContent(nullptr);
1547 DCTile::DCTile(DCLayerTree
* aDCLayerTree
) : mDCLayerTree(aDCLayerTree
) {}
1549 DCTile::~DCTile() {}
1551 bool DCTile::Initialize(int aX
, int aY
, wr::DeviceIntSize aSize
,
1552 bool aIsVirtualSurface
, bool aIsOpaque
,
1553 RefPtr
<IDCompositionVisual2
> mSurfaceVisual
) {
1554 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
1559 mIsOpaque
= aIsOpaque
;
1560 mIsVirtualSurface
= aIsVirtualSurface
;
1561 mNeedsFullDraw
= !aIsVirtualSurface
;
1563 if (aIsVirtualSurface
) {
1564 // Initially, the entire tile is considered valid, unless it is set by
1565 // the SetTileProperties method.
1568 mValidRect
.width
= aSize
.width
;
1569 mValidRect
.height
= aSize
.height
;
1572 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1573 // Create the visual and put it in the tree under the surface visual
1574 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1576 gfxCriticalNote
<< "Failed to CreateVisual for DCTile: " << gfx::hexa(hr
);
1579 mSurfaceVisual
->AddVisual(mVisual
, false, nullptr);
1580 // Position the tile relative to the surface visual
1581 mVisual
->SetOffsetX(aX
* aSize
.width
);
1582 mVisual
->SetOffsetY(aY
* aSize
.height
);
1583 // Clip the visual so it doesn't show anything until we update it
1584 D2D_RECT_F clip
= {0, 0, 0, 0};
1585 mVisual
->SetClip(clip
);
1586 // Create the underlying pixel buffer.
1587 mCompositionSurface
= CreateCompositionSurface(aSize
, aIsOpaque
);
1588 if (!mCompositionSurface
) {
1591 hr
= mVisual
->SetContent(mCompositionSurface
);
1593 gfxCriticalNote
<< "Failed to SetContent for DCTile: " << gfx::hexa(hr
);
1601 RefPtr
<IDCompositionSurface
> DCTile::CreateCompositionSurface(
1602 wr::DeviceIntSize aSize
, bool aIsOpaque
) {
1604 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1605 const auto alphaMode
=
1606 aIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1607 RefPtr
<IDCompositionSurface
> compositionSurface
;
1609 hr
= dCompDevice
->CreateSurface(aSize
.width
, aSize
.height
,
1610 DXGI_FORMAT_R8G8B8A8_UNORM
, alphaMode
,
1611 getter_AddRefs(compositionSurface
));
1613 gfxCriticalNote
<< "Failed to CreateSurface for DCTile: " << gfx::hexa(hr
);
1616 return compositionSurface
;
1619 RefPtr
<IDCompositionSurface
> DCTile::Bind(wr::DeviceIntRect aValidRect
) {
1620 if (mVisual
!= nullptr) {
1621 // Tile owns a visual, set the size of the visual to match the portion we
1622 // want to be visible.
1623 D2D_RECT_F clip_rect
;
1624 clip_rect
.left
= aValidRect
.min
.x
;
1625 clip_rect
.top
= aValidRect
.min
.y
;
1626 clip_rect
.right
= aValidRect
.max
.x
;
1627 clip_rect
.bottom
= aValidRect
.max
.y
;
1628 mVisual
->SetClip(clip_rect
);
1630 return mCompositionSurface
;
1633 GLuint
DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1634 wr::DeviceIntRect aDirtyRect
, wr::DeviceIntPoint
* aOffset
,
1635 RefPtr
<IDCompositionSurface
> aCompositionSurface
,
1636 wr::DeviceIntPoint aSurfaceOffset
) {
1637 MOZ_ASSERT(aCompositionSurface
.get());
1640 const auto gl
= GetGLContext();
1641 RefPtr
<ID3D11Texture2D
> backBuf
;
1645 update_rect
.left
= aSurfaceOffset
.x
+ aDirtyRect
.min
.x
;
1646 update_rect
.top
= aSurfaceOffset
.y
+ aDirtyRect
.min
.y
;
1647 update_rect
.right
= aSurfaceOffset
.x
+ aDirtyRect
.max
.x
;
1648 update_rect
.bottom
= aSurfaceOffset
.y
+ aDirtyRect
.max
.y
;
1649 hr
= aCompositionSurface
->BeginDraw(&update_rect
, __uuidof(ID3D11Texture2D
),
1650 (void**)getter_AddRefs(backBuf
), &offset
);
1653 LayoutDeviceIntRect rect
= widget::WinUtils::ToIntRect(update_rect
);
1655 gfxCriticalNote
<< "DCompositionSurface::BeginDraw failed: "
1656 << gfx::hexa(hr
) << " " << rect
;
1657 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW
);
1661 // DC includes the origin of the dirty / update rect in the draw offset,
1662 // undo that here since WR expects it to be an absolute offset.
1663 offset
.x
-= aDirtyRect
.min
.x
;
1664 offset
.y
-= aDirtyRect
.min
.y
;
1666 D3D11_TEXTURE2D_DESC desc
;
1667 backBuf
->GetDesc(&desc
);
1669 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1670 const auto& egl
= gle
->mEgl
;
1672 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
1674 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
1675 const EGLint attribs
[] = {LOCAL_EGL_NONE
};
1676 mEGLImage
= egl
->fCreateImage(EGL_NO_CONTEXT
, LOCAL_EGL_D3D11_TEXTURE_ANGLE
,
1679 // Get the current FBO and RBO id, so we can restore them later
1680 GLint currentFboId
, currentRboId
;
1681 gl
->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
, ¤tFboId
);
1682 gl
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, ¤tRboId
);
1684 // Create a render buffer object that is backed by the EGL image.
1685 gl
->fGenRenderbuffers(1, &mColorRBO
);
1686 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, mColorRBO
);
1687 gl
->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, mEGLImage
);
1689 // Get or create an FBO for the specified dimensions
1690 GLuint fboId
= GetOrCreateFbo(desc
.Width
, desc
.Height
);
1692 // Attach the new renderbuffer to the FBO
1693 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
1694 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
1695 LOCAL_GL_COLOR_ATTACHMENT0
,
1696 LOCAL_GL_RENDERBUFFER
, mColorRBO
);
1698 // Restore previous FBO and RBO bindings
1699 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, currentFboId
);
1700 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, currentRboId
);
1702 aOffset
->x
= offset
.x
;
1703 aOffset
->y
= offset
.y
;
1708 void DCLayerTree::DestroyEGLSurface() {
1709 const auto gl
= GetGLContext();
1712 gl
->fDeleteRenderbuffers(1, &mColorRBO
);
1717 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1718 const auto& egl
= gle
->mEgl
;
1719 egl
->fDestroyImage(mEGLImage
);
1720 mEGLImage
= EGL_NO_IMAGE
;
1726 color::ColorProfileDesc
DCLayerTree::QueryOutputColorProfile() {
1727 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
1728 // but we do need gfxPlatform::GetCMSOutputProfile().
1729 // So we steal what we need through the window:
1730 const auto outputProfileData
=
1731 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
1733 const auto qcmsProfile
= qcms_profile_from_memory(
1734 outputProfileData
.Elements(), outputProfileData
.Length());
1735 MOZ_ASSERT(qcmsProfile
);
1736 const auto release
=
1737 MakeScopeExit([&]() { qcms_profile_release(qcmsProfile
); });
1739 const auto ret
= color::ColorProfileDesc::From(*qcmsProfile
);
1740 bool print
= gfxEnv::MOZ_GL_SPEW();
1742 const auto gammaGuess
= color::GuessGamma(ret
.linearFromTf
.r
);
1744 "Display profile:\n"
1745 " Approx Gamma: %f\n"
1746 " XYZ-D65 Red : %f, %f, %f\n"
1747 " XYZ-D65 Green: %f, %f, %f\n"
1748 " XYZ-D65 Blue : %f, %f, %f\n",
1749 gammaGuess
, ret
.xyzd65FromLinearRgb
.at(0, 0),
1750 ret
.xyzd65FromLinearRgb
.at(0, 1), ret
.xyzd65FromLinearRgb
.at(0, 2),
1752 ret
.xyzd65FromLinearRgb
.at(1, 0), ret
.xyzd65FromLinearRgb
.at(1, 1),
1753 ret
.xyzd65FromLinearRgb
.at(1, 2),
1755 ret
.xyzd65FromLinearRgb
.at(2, 0), ret
.xyzd65FromLinearRgb
.at(2, 1),
1756 ret
.xyzd65FromLinearRgb
.at(2, 2));
1762 inline D2D1_MATRIX_5X4_F
to_D2D1_MATRIX_5X4_F(const color::mat4
& m
) {
1763 return D2D1_MATRIX_5X4_F
{{{
1787 ColorManagementChain
ColorManagementChain::From(
1788 IDCompositionDevice3
& dcomp
,
1789 const color::ColorProfileConversionDesc
& conv
) {
1790 auto ret
= ColorManagementChain
{};
1792 #if !defined(MOZ_MINGW_DCOMP_H_OLD)
1794 const auto Append
= [&](const RefPtr
<IDCompositionFilterEffect
>& afterLast
) {
1796 afterLast
->SetInput(0, ret
.last
, 0);
1798 ret
.last
= afterLast
;
1801 const auto MaybeAppendColorMatrix
= [&](const color::mat4
& m
) {
1802 RefPtr
<IDCompositionColorMatrixEffect
> e
;
1803 if (approx(m
, color::mat4::Identity())) return e
;
1804 dcomp
.CreateColorMatrixEffect(getter_AddRefs(e
));
1807 e
->SetMatrix(to_D2D1_MATRIX_5X4_F(m
));
1811 const auto MaybeAppendTableTransfer
= [&](const color::RgbTransferTables
& t
) {
1812 RefPtr
<IDCompositionTableTransferEffect
> e
;
1813 if (!t
.r
.size() && !t
.g
.size() && !t
.b
.size()) return e
;
1814 dcomp
.CreateTableTransferEffect(getter_AddRefs(e
));
1817 e
->SetRedTable(t
.r
.data(), t
.r
.size());
1818 e
->SetGreenTable(t
.g
.data(), t
.g
.size());
1819 e
->SetBlueTable(t
.b
.data(), t
.b
.size());
1824 ret
.srcRgbFromSrcYuv
= MaybeAppendColorMatrix(conv
.srcRgbFromSrcYuv
);
1825 ret
.srcLinearFromSrcTf
= MaybeAppendTableTransfer(conv
.srcLinearFromSrcTf
);
1826 ret
.dstLinearFromSrcLinear
=
1827 MaybeAppendColorMatrix(color::mat4(conv
.dstLinearFromSrcLinear
));
1828 ret
.dstTfFromDstLinear
= MaybeAppendTableTransfer(conv
.dstTfFromDstLinear
);
1830 #endif // !defined(MOZ_MINGW_DCOMP_H_OLD)
1835 ColorManagementChain::~ColorManagementChain() = default;
1838 } // namespace mozilla