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 #if (_WIN32_WINNT < _WIN32_WINNT_WIN10)
13 # define XSTR(x) STR(x)
17 # pragma message "IDCompositionFilterEffect in dcomp.h requires _WIN32_WINNT >= _WIN32_WINNT_WIN10."
18 // Pedantically, it actually requires _WIN32_WINNT_WINTHRESHOLD, but that's the
19 // same as _WIN32_WINNT_WIN10.
21 # pragma message "Forcing NTDDI_VERSION " XSTR(NTDDI_VERSION) " -> " XSTR(NTDDI_WIN10)
23 # define NTDDI_VERSION NTDDI_WIN10
25 # pragma message "Forcing _WIN32_WINNT " XSTR(_WIN32_WINNT) " -> " XSTR(_WIN32_WINNT_WIN10)
27 # define _WIN32_WINNT _WIN32_WINNT_WIN10
42 #include "gfxWindowsPlatform.h"
43 #include "GLContext.h"
44 #include "GLContextEGL.h"
45 #include "mozilla/gfx/DeviceManagerDx.h"
46 #include "mozilla/gfx/Logging.h"
47 #include "mozilla/gfx/gfxVars.h"
48 #include "mozilla/gfx/GPUParent.h"
49 #include "mozilla/gfx/Matrix.h"
50 #include "mozilla/StaticPrefs_gfx.h"
51 #include "mozilla/webrender/RenderD3D11TextureHost.h"
52 #include "mozilla/webrender/RenderDcompSurfaceTextureHost.h"
53 #include "mozilla/webrender/RenderTextureHost.h"
54 #include "mozilla/webrender/RenderThread.h"
55 #include "mozilla/WindowsVersion.h"
56 #include "mozilla/Telemetry.h"
57 #include "nsPrintfCString.h"
63 extern LazyLogModule gRenderThreadLog
;
64 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
66 #define LOG_H(msg, ...) \
67 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
68 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
70 UniquePtr
<GpuOverlayInfo
> DCLayerTree::sGpuOverlayInfo
;
73 UniquePtr
<DCLayerTree
> DCLayerTree::Create(gl::GLContext
* aGL
,
75 ID3D11Device
* aDevice
,
76 ID3D11DeviceContext
* aCtx
,
77 HWND aHwnd
, nsACString
& aError
) {
78 RefPtr
<IDCompositionDevice2
> dCompDevice
=
79 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
81 aError
.Assign("DCLayerTree(no device)"_ns
);
86 MakeUnique
<DCLayerTree
>(aGL
, aEGLConfig
, aDevice
, aCtx
, dCompDevice
);
87 if (!layerTree
->Initialize(aHwnd
, aError
)) {
94 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo
= nullptr; }
96 DCLayerTree::DCLayerTree(gl::GLContext
* aGL
, EGLConfig aEGLConfig
,
97 ID3D11Device
* aDevice
, ID3D11DeviceContext
* aCtx
,
98 IDCompositionDevice2
* aCompositionDevice
)
100 mEGLConfig(aEGLConfig
),
103 mCompositionDevice(aCompositionDevice
),
104 mDebugCounter(false),
105 mDebugVisualRedrawRegions(false),
106 mEGLImage(EGL_NO_IMAGE
),
108 mPendingCommit(false) {
109 LOG("DCLayerTree::DCLayerTree()");
112 DCLayerTree::~DCLayerTree() {
113 LOG("DCLayerTree::~DCLayerTree()");
115 ReleaseNativeCompositorResources();
118 void DCLayerTree::ReleaseNativeCompositorResources() {
119 const auto gl
= GetGLContext();
123 // Delete any cached FBO objects
124 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
125 gl
->fDeleteRenderbuffers(1, &it
->depthRboId
);
126 gl
->fDeleteFramebuffers(1, &it
->fboId
);
130 bool DCLayerTree::Initialize(HWND aHwnd
, nsACString
& aError
) {
133 RefPtr
<IDCompositionDesktopDevice
> desktopDevice
;
134 hr
= mCompositionDevice
->QueryInterface(
135 (IDCompositionDesktopDevice
**)getter_AddRefs(desktopDevice
));
137 aError
.Assign(nsPrintfCString(
138 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr
));
142 hr
= desktopDevice
->CreateTargetForHwnd(aHwnd
, TRUE
,
143 getter_AddRefs(mCompositionTarget
));
145 aError
.Assign(nsPrintfCString(
146 "DCLayerTree(create DCompositionTarget failed %lx)", hr
));
150 hr
= mCompositionDevice
->CreateVisual(getter_AddRefs(mRootVisual
));
152 aError
.Assign(nsPrintfCString(
153 "DCLayerTree(create root DCompositionVisual failed %lx)", hr
));
158 mCompositionDevice
->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual
));
160 aError
.Assign(nsPrintfCString(
161 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr
));
165 if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) {
166 if (!InitializeVideoOverlaySupport()) {
167 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY
);
170 if (!sGpuOverlayInfo
) {
171 // Set default if sGpuOverlayInfo was not set.
172 sGpuOverlayInfo
= MakeUnique
<GpuOverlayInfo
>();
175 // Initialize SwapChainInfo
176 SupportsSwapChainTearing();
178 mCompositionTarget
->SetRoot(mRootVisual
);
179 // Set interporation mode to nearest, to ensure 1:1 sampling.
180 // By default, a visual inherits the interpolation mode of the parent visual.
181 // If no visuals set the interpolation mode, the default for the entire visual
182 // tree is nearest neighbor interpolation.
183 mRootVisual
->SetBitmapInterpolationMode(
184 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
188 bool FlagsSupportsOverlays(UINT flags
) {
189 return (flags
& (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
|
190 DXGI_OVERLAY_SUPPORT_FLAG_SCALING
));
193 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
194 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat
,
195 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace
,
196 RefPtr
<IDXGIOutput
> aOutput
,
197 RefPtr
<ID3D11Device
> aD3d11Device
) {
198 UINT colorSpaceSupportFlags
= 0;
199 RefPtr
<IDXGIOutput4
> output4
;
201 if (FAILED(aOutput
->QueryInterface(__uuidof(IDXGIOutput4
),
202 getter_AddRefs(output4
)))) {
206 if (FAILED(output4
->CheckOverlayColorSpaceSupport(
207 aDxgiFormat
, aDxgiColorSpace
, aD3d11Device
,
208 &colorSpaceSupportFlags
))) {
212 return (colorSpaceSupportFlags
&
213 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT
);
216 bool DCLayerTree::InitializeVideoOverlaySupport() {
217 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
221 hr
= mDevice
->QueryInterface(
222 (ID3D11VideoDevice
**)getter_AddRefs(mVideoDevice
));
224 gfxCriticalNote
<< "Failed to get D3D11VideoDevice: " << gfx::hexa(hr
);
229 mCtx
->QueryInterface((ID3D11VideoContext
**)getter_AddRefs(mVideoContext
));
231 gfxCriticalNote
<< "Failed to get D3D11VideoContext: " << gfx::hexa(hr
);
235 if (sGpuOverlayInfo
) {
239 UniquePtr
<GpuOverlayInfo
> info
= MakeUnique
<GpuOverlayInfo
>();
241 RefPtr
<IDXGIDevice
> dxgiDevice
;
242 RefPtr
<IDXGIAdapter
> adapter
;
243 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
244 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
248 RefPtr
<IDXGIOutput
> output
;
249 if (FAILED(adapter
->EnumOutputs(i
++, getter_AddRefs(output
)))) {
252 RefPtr
<IDXGIOutput3
> output3
;
253 if (FAILED(output
->QueryInterface(__uuidof(IDXGIOutput3
),
254 getter_AddRefs(output3
)))) {
258 output3
->CheckOverlaySupport(DXGI_FORMAT_NV12
, mDevice
,
259 &info
->mNv12OverlaySupportFlags
);
260 output3
->CheckOverlaySupport(DXGI_FORMAT_YUY2
, mDevice
,
261 &info
->mYuy2OverlaySupportFlags
);
262 output3
->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM
, mDevice
,
263 &info
->mBgra8OverlaySupportFlags
);
264 output3
->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM
, mDevice
,
265 &info
->mRgb10a2OverlaySupportFlags
);
267 if (FlagsSupportsOverlays(info
->mNv12OverlaySupportFlags
)) {
268 // NV12 format is preferred if it's supported.
269 info
->mOverlayFormatUsed
= DXGI_FORMAT_NV12
;
270 info
->mSupportsHardwareOverlays
= true;
273 if (!info
->mSupportsHardwareOverlays
&&
274 FlagsSupportsOverlays(info
->mYuy2OverlaySupportFlags
)) {
275 // If NV12 isn't supported, fallback to YUY2 if it's supported.
276 info
->mOverlayFormatUsed
= DXGI_FORMAT_YUY2
;
277 info
->mSupportsHardwareOverlays
= true;
280 // RGB10A2 overlay is used for displaying HDR content. In Intel's
281 // platform, RGB10A2 overlay is enabled only when
282 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
283 if (FlagsSupportsOverlays(info
->mRgb10a2OverlaySupportFlags
)) {
284 if (!CheckOverlayColorSpaceSupport(
285 DXGI_FORMAT_R10G10B10A2_UNORM
,
286 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
, output
, mDevice
))
287 info
->mRgb10a2OverlaySupportFlags
= 0;
290 // Early out after the first output that reports overlay support. All
291 // outputs are expected to report the same overlay support according to
292 // Microsoft's WDDM documentation:
293 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
294 if (info
->mSupportsHardwareOverlays
) {
299 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
300 info
->mOverlayFormatUsed
= DXGI_FORMAT_B8G8R8A8_UNORM
;
301 info
->mSupportsHardwareOverlays
= false;
304 info
->mSupportsOverlays
= info
->mSupportsHardwareOverlays
;
306 sGpuOverlayInfo
= std::move(info
);
308 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
309 gpuParent
->NotifyOverlayInfo(GetOverlayInfo());
315 DCSurface
* DCLayerTree::GetSurface(wr::NativeSurfaceId aId
) const {
316 auto surface_it
= mDCSurfaces
.find(aId
);
317 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
318 return surface_it
->second
.get();
321 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1
* aSwapChain
) {
322 LOG("DCLayerTree::SetDefaultSwapChain()");
324 mRootVisual
->AddVisual(mDefaultSwapChainVisual
, TRUE
, nullptr);
325 mDefaultSwapChainVisual
->SetContent(aSwapChain
);
326 // Default SwapChain's visual does not need linear interporation.
327 mDefaultSwapChainVisual
->SetBitmapInterpolationMode(
328 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
329 mPendingCommit
= true;
332 void DCLayerTree::MaybeUpdateDebug() {
333 bool updated
= false;
334 updated
|= MaybeUpdateDebugCounter();
335 updated
|= MaybeUpdateDebugVisualRedrawRegions();
337 mPendingCommit
= true;
341 void DCLayerTree::MaybeCommit() {
342 if (!mPendingCommit
) {
345 mCompositionDevice
->Commit();
348 void DCLayerTree::WaitForCommitCompletion() {
349 mCompositionDevice
->WaitForCommitCompletion();
352 void DCLayerTree::DisableNativeCompositor() {
353 MOZ_ASSERT(mCurrentSurface
.isNothing());
354 MOZ_ASSERT(mCurrentLayers
.empty());
356 ReleaseNativeCompositorResources();
358 mRootVisual
->RemoveAllVisuals();
361 bool DCLayerTree::MaybeUpdateDebugCounter() {
362 bool debugCounter
= StaticPrefs::gfx_webrender_debug_dcomp_counter();
363 if (mDebugCounter
== debugCounter
) {
367 RefPtr
<IDCompositionDeviceDebug
> debugDevice
;
368 HRESULT hr
= mCompositionDevice
->QueryInterface(
369 (IDCompositionDeviceDebug
**)getter_AddRefs(debugDevice
));
375 debugDevice
->EnableDebugCounters();
377 debugDevice
->DisableDebugCounters();
380 mDebugCounter
= debugCounter
;
384 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
385 bool debugVisualRedrawRegions
=
386 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
387 if (mDebugVisualRedrawRegions
== debugVisualRedrawRegions
) {
391 RefPtr
<IDCompositionVisualDebug
> visualDebug
;
392 HRESULT hr
= mRootVisual
->QueryInterface(
393 (IDCompositionVisualDebug
**)getter_AddRefs(visualDebug
));
398 if (debugVisualRedrawRegions
) {
399 visualDebug
->EnableRedrawRegions();
401 visualDebug
->DisableRedrawRegions();
404 mDebugVisualRedrawRegions
= debugVisualRedrawRegions
;
408 void DCLayerTree::CompositorBeginFrame() { mCurrentFrame
++; }
410 void DCLayerTree::CompositorEndFrame() {
411 auto start
= TimeStamp::Now();
412 // Check if the visual tree of surfaces is the same as last frame.
413 bool same
= mPrevLayers
== mCurrentLayers
;
416 // If not, we need to rebuild the visual tree. Note that addition or
417 // removal of tiles no longer needs to rebuild the main visual tree
418 // here, since they are added as children of the surface visual.
419 mRootVisual
->RemoveAllVisuals();
422 for (auto it
= mCurrentLayers
.begin(); it
!= mCurrentLayers
.end(); ++it
) {
423 auto surface_it
= mDCSurfaces
.find(*it
);
424 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
425 const auto surface
= surface_it
->second
.get();
426 // Ensure surface is trimmed to updated tile valid rects
427 surface
->UpdateAllocatedRect();
429 // Add surfaces in z-order they were added to the scene.
430 const auto visual
= surface
->GetVisual();
431 mRootVisual
->AddVisual(visual
, false, nullptr);
435 mPrevLayers
.swap(mCurrentLayers
);
436 mCurrentLayers
.clear();
438 mCompositionDevice
->Commit();
440 auto end
= TimeStamp::Now();
441 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
442 (end
- start
).ToMilliseconds() * 10.);
444 // Remove any framebuffers that haven't been
445 // used in the last 60 frames.
447 // This should use nsTArray::RemoveElementsBy once
448 // CachedFrameBuffer is able to properly destroy
449 // itself in the destructor.
450 const auto gl
= GetGLContext();
451 for (uint32_t i
= 0, len
= mFrameBuffers
.Length(); i
< len
; ++i
) {
452 auto& fb
= mFrameBuffers
[i
];
453 if ((mCurrentFrame
- fb
.lastFrameUsed
) > 60) {
454 gl
->fDeleteRenderbuffers(1, &fb
.depthRboId
);
455 gl
->fDeleteFramebuffers(1, &fb
.fboId
);
456 mFrameBuffers
.UnorderedRemoveElementAt(i
);
457 --i
; // Examine the element again, if necessary.
463 void DCLayerTree::Bind(wr::NativeTileId aId
, wr::DeviceIntPoint
* aOffset
,
464 uint32_t* aFboId
, wr::DeviceIntRect aDirtyRect
,
465 wr::DeviceIntRect aValidRect
) {
466 auto surface
= GetSurface(aId
.surface_id
);
467 auto tile
= surface
->GetTile(aId
.x
, aId
.y
);
468 wr::DeviceIntPoint targetOffset
{0, 0};
470 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
471 // IDCompositionVirtualSurface owned by the DCSurface.
472 RefPtr
<IDCompositionSurface
> compositionSurface
;
473 if (surface
->mIsVirtualSurface
) {
474 gfx::IntRect
validRect(aValidRect
.min
.x
, aValidRect
.min
.y
,
475 aValidRect
.width(), aValidRect
.height());
476 if (!tile
->mValidRect
.IsEqualEdges(validRect
)) {
477 tile
->mValidRect
= validRect
;
478 surface
->DirtyAllocatedRect();
480 wr::DeviceIntSize tileSize
= surface
->GetTileSize();
481 compositionSurface
= surface
->GetCompositionSurface();
482 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
483 targetOffset
.x
= virtualOffset
.x
+ tileSize
.width
* aId
.x
;
484 targetOffset
.y
= virtualOffset
.y
+ tileSize
.height
* aId
.y
;
486 compositionSurface
= tile
->Bind(aValidRect
);
489 if (tile
->mNeedsFullDraw
) {
490 // dcomp requires that the first BeginDraw on a non-virtual surface is the
491 // full size of the pixel buffer.
492 auto tileSize
= surface
->GetTileSize();
493 aDirtyRect
.min
.x
= 0;
494 aDirtyRect
.min
.y
= 0;
495 aDirtyRect
.max
.x
= tileSize
.width
;
496 aDirtyRect
.max
.y
= tileSize
.height
;
497 tile
->mNeedsFullDraw
= false;
500 *aFboId
= CreateEGLSurfaceForCompositionSurface(
501 aDirtyRect
, aOffset
, compositionSurface
, targetOffset
);
502 mCurrentSurface
= Some(compositionSurface
);
505 void DCLayerTree::Unbind() {
506 if (mCurrentSurface
.isNothing()) {
510 RefPtr
<IDCompositionSurface
> surface
= mCurrentSurface
.ref();
514 mCurrentSurface
= Nothing();
517 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId
,
518 wr::DeviceIntPoint aVirtualOffset
,
519 wr::DeviceIntSize aTileSize
, bool aIsOpaque
) {
520 auto it
= mDCSurfaces
.find(aId
);
521 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
522 if (it
!= mDCSurfaces
.end()) {
523 // DCSurface already exists.
527 // Tile size needs to be positive.
528 if (aTileSize
.width
<= 0 || aTileSize
.height
<= 0) {
529 gfxCriticalNote
<< "TileSize is not positive aId: " << wr::AsUint64(aId
)
530 << " aTileSize(" << aTileSize
.width
<< ","
531 << aTileSize
.height
<< ")";
534 bool isVirtualSurface
=
535 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
536 auto surface
= MakeUnique
<DCSurface
>(aTileSize
, aVirtualOffset
,
537 isVirtualSurface
, aIsOpaque
, this);
538 if (!surface
->Initialize()) {
539 gfxCriticalNote
<< "Failed to initialize DCSurface: " << wr::AsUint64(aId
);
543 mDCSurfaces
[aId
] = std::move(surface
);
546 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId
,
548 auto it
= mDCSurfaces
.find(aId
);
549 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
551 auto surface
= MakeUnique
<DCExternalSurfaceWrapper
>(aIsOpaque
, this);
552 if (!surface
->Initialize()) {
553 gfxCriticalNote
<< "Failed to initialize DCExternalSurfaceWrapper: "
554 << wr::AsUint64(aId
);
558 mDCSurfaces
[aId
] = std::move(surface
);
561 void DCLayerTree::DestroySurface(NativeSurfaceId aId
) {
562 auto surface_it
= mDCSurfaces
.find(aId
);
563 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
564 auto surface
= surface_it
->second
.get();
566 mRootVisual
->RemoveVisual(surface
->GetVisual());
567 mDCSurfaces
.erase(surface_it
);
570 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
571 auto surface
= GetSurface(aId
);
572 surface
->CreateTile(aX
, aY
);
575 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
576 auto surface
= GetSurface(aId
);
577 surface
->DestroyTile(aX
, aY
);
580 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId
,
581 wr::ExternalImageId aExternalImage
) {
582 auto surface_it
= mDCSurfaces
.find(aId
);
583 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
584 surface_it
->second
->AttachExternalImage(aExternalImage
);
587 void DCExternalSurfaceWrapper::AttachExternalImage(
588 wr::ExternalImageId aExternalImage
) {
589 if (auto* surface
= EnsureSurfaceForExternalImage(aExternalImage
)) {
590 surface
->AttachExternalImage(aExternalImage
);
596 template <class FromT
>
597 [[nodiscard
]] static inline RefPtr
<ToT
> From(FromT
* const from
) {
599 (void)from
->QueryInterface(static_cast<ToT
**>(getter_AddRefs(to
)));
604 DCSurface
* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
605 wr::ExternalImageId aExternalImage
) {
607 return mSurface
.get();
610 // Create a new surface based on the texture type.
611 RenderTextureHost
* texture
=
612 RenderThread::Get()->GetRenderTexture(aExternalImage
);
613 if (texture
&& texture
->AsRenderDXGITextureHost()) {
614 mSurface
.reset(new DCSurfaceVideo(mIsOpaque
, mDCLayerTree
));
615 if (!mSurface
->Initialize()) {
616 gfxCriticalNote
<< "Failed to initialize DCSurfaceVideo: "
617 << wr::AsUint64(aExternalImage
);
620 } else if (texture
&& texture
->AsRenderDcompSurfaceTextureHost()) {
621 mSurface
.reset(new DCSurfaceHandle(mIsOpaque
, mDCLayerTree
));
622 if (!mSurface
->Initialize()) {
623 gfxCriticalNote
<< "Failed to initialize DCSurfaceHandle: "
624 << wr::AsUint64(aExternalImage
);
629 gfxCriticalNote
<< "Failed to create a surface for external image: "
630 << gfx::hexa(texture
);
633 const auto textureSwgl
= texture
->AsRenderTextureHostSWGL();
634 MOZ_ASSERT(textureSwgl
); // Covered above.
636 // Add surface's visual which will contain video data to our root visual.
637 const auto surfaceVisual
= mSurface
->GetVisual();
638 mVisual
->AddVisual(surfaceVisual
, true, nullptr);
641 // Apply color management.
644 const auto cmsMode
= GfxColorManagementMode();
645 if (cmsMode
== CMSMode::Off
) return;
647 const auto dcomp
= mDCLayerTree
->GetCompositionDevice();
648 const auto dcomp3
= QI
<IDCompositionDevice3
>::From(dcomp
);
651 "No IDCompositionDevice3, cannot use dcomp for color management.");
657 const auto cspace
= [&]() {
658 const auto rangedCspace
= textureSwgl
->GetYUVColorSpace();
659 const auto info
= FromYUVRangedColorSpace(rangedCspace
);
660 auto ret
= ToColorSpace2(info
.space
);
661 if (ret
== gfx::ColorSpace2::Display
&& cmsMode
== CMSMode::All
) {
662 ret
= gfx::ColorSpace2::SRGB
;
667 const bool rec709GammaAsSrgb
=
668 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
669 const bool rec2020GammaAsRec709
=
670 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
672 auto cspaceDesc
= color::ColorspaceDesc
{};
674 case gfx::ColorSpace2::Display
:
675 return; // No color management needed!
676 case gfx::ColorSpace2::SRGB
:
677 cspaceDesc
.chrom
= color::Chromaticities::Srgb();
678 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
681 case gfx::ColorSpace2::DISPLAY_P3
:
682 cspaceDesc
.chrom
= color::Chromaticities::DisplayP3();
683 cspaceDesc
.tf
= color::PiecewiseGammaDesc::DisplayP3();
686 case gfx::ColorSpace2::BT601_525
:
687 cspaceDesc
.chrom
= color::Chromaticities::Rec601_525_Ntsc();
688 if (rec709GammaAsSrgb
) {
689 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
691 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
695 case gfx::ColorSpace2::BT709
:
696 cspaceDesc
.chrom
= color::Chromaticities::Rec709();
697 if (rec709GammaAsSrgb
) {
698 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
700 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
704 case gfx::ColorSpace2::BT2020
:
705 cspaceDesc
.chrom
= color::Chromaticities::Rec2020();
706 if (rec2020GammaAsRec709
&& rec709GammaAsSrgb
) {
707 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
708 } else if (rec2020GammaAsRec709
) {
709 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
711 // Just Rec709 with slightly more precision.
712 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec2020_12bit();
717 const auto cprofileIn
= color::ColorProfileDesc::From(cspaceDesc
);
718 auto cprofileOut
= mDCLayerTree
->OutputColorProfile();
719 bool pretendSrgb
= StaticPrefs::gfx_color_management_native_srgb();
721 cprofileOut
= color::ColorProfileDesc::From({
722 color::Chromaticities::Srgb(),
723 color::PiecewiseGammaDesc::Srgb(),
726 const auto conversion
= color::ColorProfileConversionDesc::From({
733 auto chain
= ColorManagementChain::From(*dcomp3
, conversion
);
734 mCManageChain
= Some(chain
);
736 surfaceVisual
->SetEffect(mCManageChain
->last
.get());
739 return mSurface
.get();
742 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix
& aTransform
) {
743 MOZ_ASSERT(mSurface
);
744 if (auto* surface
= mSurface
->AsDCSurfaceVideo()) {
745 if (surface
->CalculateSwapChainSize(aTransform
)) {
746 surface
->PresentVideo();
748 } else if (auto* surface
= mSurface
->AsDCSurfaceHandle()) {
749 surface
->PresentSurfaceHandle();
753 template <typename T
>
754 static inline D2D1_RECT_F
D2DRect(const T
& aRect
) {
755 return D2D1::RectF(aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost());
758 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const gfx::Matrix
& aTransform
) {
759 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
, aTransform
._21
,
760 aTransform
._22
, aTransform
._31
, aTransform
._32
);
763 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId
,
764 const wr::CompositorSurfaceTransform
& aTransform
,
765 wr::DeviceIntRect aClipRect
,
766 wr::ImageRendering aImageRendering
) {
767 auto it
= mDCSurfaces
.find(aId
);
768 MOZ_RELEASE_ASSERT(it
!= mDCSurfaces
.end());
769 const auto surface
= it
->second
.get();
770 const auto visual
= surface
->GetVisual();
772 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
774 float sx
= aTransform
.scale
.x
;
775 float sy
= aTransform
.scale
.y
;
776 float tx
= aTransform
.offset
.x
;
777 float ty
= aTransform
.offset
.y
;
778 gfx::Matrix
transform(sx
, 0.0, 0.0, sy
, tx
, ty
);
780 surface
->PresentExternalSurface(transform
);
782 transform
.PreTranslate(-virtualOffset
.x
, -virtualOffset
.y
);
784 // The DirectComposition API applies clipping *before* any
785 // transforms/offset, whereas we want the clip applied after. Right now, we
786 // only support rectilinear transforms, and then we transform our clip into
787 // pre-transform coordinate space for it to be applied there.
788 // DirectComposition does have an option for pre-transform clipping, if you
789 // create an explicit IDCompositionEffectGroup object and set a 3D transform
790 // on that. I suspect that will perform worse though, so we should only do
791 // that for complex transforms (which are never provided right now).
792 MOZ_ASSERT(transform
.IsRectilinear());
793 gfx::Rect clip
= transform
.Inverse().TransformBounds(gfx::Rect(
794 aClipRect
.min
.x
, aClipRect
.min
.y
, aClipRect
.width(), aClipRect
.height()));
795 // Set the clip rect - converting from world space to the pre-offset space
796 // that DC requires for rectangle clips.
797 visual
->SetClip(D2DRect(clip
));
799 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
800 // the D3D API level (unless we QI to IDCompositionVisual3, which might
801 // not be available?).
802 // Should we assert here, or restrict at the WR API level.
803 visual
->SetTransform(D2DMatrix(transform
));
805 if (aImageRendering
== wr::ImageRendering::Auto
) {
806 visual
->SetBitmapInterpolationMode(
807 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR
);
809 visual
->SetBitmapInterpolationMode(
810 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
813 mCurrentLayers
.push_back(aId
);
816 GLuint
DCLayerTree::GetOrCreateFbo(int aWidth
, int aHeight
) {
817 const auto gl
= GetGLContext();
820 // Check if we have a cached FBO with matching dimensions
821 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
822 if (it
->width
== aWidth
&& it
->height
== aHeight
) {
824 it
->lastFrameUsed
= mCurrentFrame
;
829 // If not, create a new FBO with attached depth buffer
831 // Create the depth buffer
833 gl
->fGenRenderbuffers(1, &depthRboId
);
834 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, depthRboId
);
835 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, LOCAL_GL_DEPTH_COMPONENT24
,
838 // Create the framebuffer and attach the depth buffer to it
839 gl
->fGenFramebuffers(1, &fboId
);
840 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
841 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
842 LOCAL_GL_DEPTH_ATTACHMENT
,
843 LOCAL_GL_RENDERBUFFER
, depthRboId
);
845 // Store this in the cache for future calls.
846 // TODO(gw): Maybe we should periodically scan this list and remove old
848 // haven't been used for some time?
849 DCLayerTree::CachedFrameBuffer frame_buffer_info
;
850 frame_buffer_info
.width
= aWidth
;
851 frame_buffer_info
.height
= aHeight
;
852 frame_buffer_info
.fboId
= fboId
;
853 frame_buffer_info
.depthRboId
= depthRboId
;
854 frame_buffer_info
.lastFrameUsed
= mCurrentFrame
;
855 mFrameBuffers
.AppendElement(frame_buffer_info
);
861 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize
& aInputSize
,
862 const gfx::IntSize
& aOutputSize
) {
865 if (!mVideoDevice
|| !mVideoContext
) {
869 if (mVideoProcessor
&& (aInputSize
<= mVideoInputSize
) &&
870 (aOutputSize
<= mVideoOutputSize
)) {
874 mVideoProcessor
= nullptr;
875 mVideoProcessorEnumerator
= nullptr;
877 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc
= {};
878 desc
.InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE
;
879 desc
.InputFrameRate
.Numerator
= 60;
880 desc
.InputFrameRate
.Denominator
= 1;
881 desc
.InputWidth
= aInputSize
.width
;
882 desc
.InputHeight
= aInputSize
.height
;
883 desc
.OutputFrameRate
.Numerator
= 60;
884 desc
.OutputFrameRate
.Denominator
= 1;
885 desc
.OutputWidth
= aOutputSize
.width
;
886 desc
.OutputHeight
= aOutputSize
.height
;
887 desc
.Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
;
889 hr
= mVideoDevice
->CreateVideoProcessorEnumerator(
890 &desc
, getter_AddRefs(mVideoProcessorEnumerator
));
892 gfxCriticalNote
<< "Failed to create VideoProcessorEnumerator: "
897 hr
= mVideoDevice
->CreateVideoProcessor(mVideoProcessorEnumerator
, 0,
898 getter_AddRefs(mVideoProcessor
));
900 mVideoProcessor
= nullptr;
901 mVideoProcessorEnumerator
= nullptr;
902 gfxCriticalNote
<< "Failed to create VideoProcessor: " << gfx::hexa(hr
);
906 // Reduce power cosumption
907 // By default, the driver might perform certain processing tasks
909 mVideoContext
->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor
, 0,
912 mVideoInputSize
= aInputSize
;
913 mVideoOutputSize
= aOutputSize
;
918 bool DCLayerTree::SupportsHardwareOverlays() {
919 return sGpuOverlayInfo
->mSupportsHardwareOverlays
;
922 bool DCLayerTree::SupportsSwapChainTearing() {
923 RefPtr
<ID3D11Device
> device
= mDevice
;
924 static const bool supported
= [device
] {
925 RefPtr
<IDXGIDevice
> dxgiDevice
;
926 RefPtr
<IDXGIAdapter
> adapter
;
927 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
928 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
930 RefPtr
<IDXGIFactory5
> dxgiFactory
;
931 HRESULT hr
= adapter
->GetParent(
932 IID_PPV_ARGS((IDXGIFactory5
**)getter_AddRefs(dxgiFactory
)));
937 BOOL presentAllowTearing
= FALSE
;
938 hr
= dxgiFactory
->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING
,
939 &presentAllowTearing
,
940 sizeof(presentAllowTearing
));
945 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
946 gpuParent
->NotifySwapChainInfo(
947 layers::SwapChainInfo(!!presentAllowTearing
));
948 } else if (XRE_IsParentProcess()) {
949 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
951 return !!presentAllowTearing
;
956 DXGI_FORMAT
DCLayerTree::GetOverlayFormatForSDR() {
957 return sGpuOverlayInfo
->mOverlayFormatUsed
;
960 static layers::OverlaySupportType
FlagsToOverlaySupportType(
961 UINT aFlags
, bool aSoftwareOverlaySupported
) {
962 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_SCALING
) {
963 return layers::OverlaySupportType::Scaling
;
965 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
) {
966 return layers::OverlaySupportType::Direct
;
968 if (aSoftwareOverlaySupported
) {
969 return layers::OverlaySupportType::Software
;
971 return layers::OverlaySupportType::None
;
974 layers::OverlayInfo
DCLayerTree::GetOverlayInfo() {
975 layers::OverlayInfo info
;
977 info
.mSupportsOverlays
= sGpuOverlayInfo
->mSupportsHardwareOverlays
;
979 FlagsToOverlaySupportType(sGpuOverlayInfo
->mNv12OverlaySupportFlags
,
980 /* aSoftwareOverlaySupported */ false);
982 FlagsToOverlaySupportType(sGpuOverlayInfo
->mYuy2OverlaySupportFlags
,
983 /* aSoftwareOverlaySupported */ false);
985 FlagsToOverlaySupportType(sGpuOverlayInfo
->mBgra8OverlaySupportFlags
,
986 /* aSoftwareOverlaySupported */ true);
987 info
.mRgb10a2Overlay
=
988 FlagsToOverlaySupportType(sGpuOverlayInfo
->mRgb10a2OverlaySupportFlags
,
989 /* aSoftwareOverlaySupported */ false);
994 DCSurface::DCSurface(wr::DeviceIntSize aTileSize
,
995 wr::DeviceIntPoint aVirtualOffset
, bool aIsVirtualSurface
,
996 bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
997 : mIsVirtualSurface(aIsVirtualSurface
),
998 mDCLayerTree(aDCLayerTree
),
999 mTileSize(aTileSize
),
1000 mIsOpaque(aIsOpaque
),
1001 mAllocatedRectDirty(true),
1002 mVirtualOffset(aVirtualOffset
) {}
1004 DCSurface::~DCSurface() {}
1006 bool DCSurface::Initialize() {
1007 // Create a visual for tiles to attach to, whether virtual or not.
1009 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1010 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1012 gfxCriticalNote
<< "Failed to create DCompositionVisual: " << gfx::hexa(hr
);
1016 // If virtual surface is enabled, create and attach to visual, in this case
1017 // the tiles won't own visuals or surfaces.
1018 if (mIsVirtualSurface
) {
1019 DXGI_ALPHA_MODE alpha_mode
=
1020 mIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1022 hr
= dCompDevice
->CreateVirtualSurface(
1023 VIRTUAL_SURFACE_SIZE
, VIRTUAL_SURFACE_SIZE
, DXGI_FORMAT_R8G8B8A8_UNORM
,
1024 alpha_mode
, getter_AddRefs(mVirtualSurface
));
1025 MOZ_ASSERT(SUCCEEDED(hr
));
1027 // Bind the surface memory to this visual
1028 hr
= mVisual
->SetContent(mVirtualSurface
);
1029 MOZ_ASSERT(SUCCEEDED(hr
));
1035 void DCSurface::CreateTile(int32_t aX
, int32_t aY
) {
1036 TileKey
key(aX
, aY
);
1037 MOZ_RELEASE_ASSERT(mDCTiles
.find(key
) == mDCTiles
.end());
1039 auto tile
= MakeUnique
<DCTile
>(mDCLayerTree
);
1040 if (!tile
->Initialize(aX
, aY
, mTileSize
, mIsVirtualSurface
, mIsOpaque
,
1042 gfxCriticalNote
<< "Failed to initialize DCTile: " << aX
<< aY
;
1046 if (mIsVirtualSurface
) {
1047 mAllocatedRectDirty
= true;
1049 mVisual
->AddVisual(tile
->GetVisual(), false, nullptr);
1052 mDCTiles
[key
] = std::move(tile
);
1055 void DCSurface::DestroyTile(int32_t aX
, int32_t aY
) {
1056 TileKey
key(aX
, aY
);
1057 if (mIsVirtualSurface
) {
1058 mAllocatedRectDirty
= true;
1060 auto tile
= GetTile(aX
, aY
);
1061 mVisual
->RemoveVisual(tile
->GetVisual());
1063 mDCTiles
.erase(key
);
1066 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty
= true; }
1068 void DCSurface::UpdateAllocatedRect() {
1069 if (mAllocatedRectDirty
) {
1070 if (mVirtualSurface
) {
1071 // The virtual surface may have holes in it (for example, an empty tile
1072 // that has no primitives). Instead of trimming to a single bounding
1073 // rect, supply the rect of each valid tile to handle this case.
1074 std::vector
<RECT
> validRects
;
1076 for (auto it
= mDCTiles
.begin(); it
!= mDCTiles
.end(); ++it
) {
1077 auto tile
= GetTile(it
->first
.mX
, it
->first
.mY
);
1080 rect
.left
= (LONG
)(mVirtualOffset
.x
+ it
->first
.mX
* mTileSize
.width
+
1081 tile
->mValidRect
.x
);
1082 rect
.top
= (LONG
)(mVirtualOffset
.y
+ it
->first
.mY
* mTileSize
.height
+
1083 tile
->mValidRect
.y
);
1084 rect
.right
= rect
.left
+ tile
->mValidRect
.width
;
1085 rect
.bottom
= rect
.top
+ tile
->mValidRect
.height
;
1087 validRects
.push_back(rect
);
1090 mVirtualSurface
->Trim(validRects
.data(), validRects
.size());
1092 // When not using a virtual surface, we still want to reset this
1093 mAllocatedRectDirty
= false;
1097 DCTile
* DCSurface::GetTile(int32_t aX
, int32_t aY
) const {
1098 TileKey
key(aX
, aY
);
1099 auto tile_it
= mDCTiles
.find(key
);
1100 MOZ_RELEASE_ASSERT(tile_it
!= mDCTiles
.end());
1101 return tile_it
->second
.get();
1104 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1105 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1108 DCSurfaceVideo::~DCSurfaceVideo() {
1109 ReleaseDecodeSwapChainResources();
1110 MOZ_ASSERT(!mSwapChainSurfaceHandle
);
1113 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat
) {
1114 if (aFormat
== DXGI_FORMAT_NV12
|| aFormat
== DXGI_FORMAT_YUY2
) {
1120 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1121 RenderTextureHost
* texture
=
1122 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1123 MOZ_RELEASE_ASSERT(texture
);
1125 if (mPrevTexture
== texture
) {
1129 // XXX if software decoded video frame format is nv12, it could be used as
1131 if (!texture
|| !texture
->AsRenderDXGITextureHost() ||
1132 texture
->AsRenderDXGITextureHost()->GetFormat() !=
1133 gfx::SurfaceFormat::NV12
) {
1134 gfxCriticalNote
<< "Unsupported RenderTexture for overlay: "
1135 << gfx::hexa(texture
);
1139 mRenderTextureHost
= texture
;
1142 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix
& aTransform
) {
1143 if (!mRenderTextureHost
) {
1144 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1148 mVideoSize
= mRenderTextureHost
->AsRenderDXGITextureHost()->GetSize(0);
1150 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1151 // DCSurfaceVideo::PresentVideo() needs to be called.
1152 bool needsToPresent
= mPrevTexture
!= mRenderTextureHost
;
1153 gfx::IntSize swapChainSize
= mVideoSize
;
1154 gfx::Matrix transform
= aTransform
;
1156 // When video is rendered to axis aligned integer rectangle, video scaling
1157 // could be done by VideoProcessor
1158 bool scaleVideoAtVideoProcessor
= false;
1159 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1160 aTransform
.PreservesAxisAlignedRectangles()) {
1161 gfx::Size scaledSize
= gfx::Size(mVideoSize
) * aTransform
.ScaleFactors();
1162 gfx::IntSize
size(int32_t(std::round(scaledSize
.width
)),
1163 int32_t(std::round(scaledSize
.height
)));
1164 if (gfx::FuzzyEqual(scaledSize
.width
, size
.width
, 0.1f
) &&
1165 gfx::FuzzyEqual(scaledSize
.height
, size
.height
, 0.1f
)) {
1166 scaleVideoAtVideoProcessor
= true;
1167 swapChainSize
= size
;
1171 if (scaleVideoAtVideoProcessor
) {
1172 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1173 // subsampled formats like NV12 must have an even width and height.
1174 if (swapChainSize
.width
% 2 == 1) {
1175 swapChainSize
.width
+= 1;
1177 if (swapChainSize
.height
% 2 == 1) {
1178 swapChainSize
.height
+= 1;
1180 transform
= gfx::Matrix::Translation(aTransform
.GetTranslation());
1183 if (!mVideoSwapChain
|| mSwapChainSize
!= swapChainSize
) {
1184 needsToPresent
= true;
1185 ReleaseDecodeSwapChainResources();
1186 // Update mSwapChainSize before creating SwapChain
1187 mSwapChainSize
= swapChainSize
;
1189 auto swapChainFormat
= GetSwapChainFormat();
1190 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1191 if (useYUVSwapChain
) {
1192 // Tries to create YUV SwapChain
1193 CreateVideoSwapChain();
1194 if (!mVideoSwapChain
) {
1195 mFailedYuvSwapChain
= true;
1196 ReleaseDecodeSwapChainResources();
1198 gfxCriticalNote
<< "Fallback to RGB SwapChain";
1201 // Tries to create RGB SwapChain
1202 if (!mVideoSwapChain
) {
1203 CreateVideoSwapChain();
1207 aTransform
= transform
;
1209 return needsToPresent
;
1212 void DCSurfaceVideo::PresentVideo() {
1213 if (!mRenderTextureHost
) {
1217 if (!mVideoSwapChain
) {
1218 gfxCriticalNote
<< "Failed to create VideoSwapChain";
1219 RenderThread::Get()->NotifyWebRenderError(
1220 wr::WebRenderError::VIDEO_OVERLAY
);
1224 mVisual
->SetContent(mVideoSwapChain
);
1226 if (!CallVideoProcessorBlt()) {
1227 auto swapChainFormat
= GetSwapChainFormat();
1228 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1229 if (useYUVSwapChain
) {
1230 mFailedYuvSwapChain
= true;
1231 ReleaseDecodeSwapChainResources();
1234 RenderThread::Get()->NotifyWebRenderError(
1235 wr::WebRenderError::VIDEO_OVERLAY
);
1240 hr
= mVideoSwapChain
->Present(0, 0);
1241 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1242 gfxCriticalNoteOnce
<< "video Present failed: " << gfx::hexa(hr
);
1245 mPrevTexture
= mRenderTextureHost
;
1248 DXGI_FORMAT
DCSurfaceVideo::GetSwapChainFormat() {
1249 if (mFailedYuvSwapChain
|| !mDCLayerTree
->SupportsHardwareOverlays()) {
1250 return DXGI_FORMAT_B8G8R8A8_UNORM
;
1252 return mDCLayerTree
->GetOverlayFormatForSDR();
1255 bool DCSurfaceVideo::CreateVideoSwapChain() {
1256 MOZ_ASSERT(mRenderTextureHost
);
1258 const auto device
= mDCLayerTree
->GetDevice();
1260 RefPtr
<IDXGIDevice
> dxgiDevice
;
1261 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1263 RefPtr
<IDXGIFactoryMedia
> dxgiFactoryMedia
;
1265 RefPtr
<IDXGIAdapter
> adapter
;
1266 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1268 IID_PPV_ARGS((IDXGIFactoryMedia
**)getter_AddRefs(dxgiFactoryMedia
)));
1271 mSwapChainSurfaceHandle
= gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1272 if (!mSwapChainSurfaceHandle
) {
1273 gfxCriticalNote
<< "Failed to create DCompSurfaceHandle";
1277 auto swapChainFormat
= GetSwapChainFormat();
1279 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1280 desc
.Width
= mSwapChainSize
.width
;
1281 desc
.Height
= mSwapChainSize
.height
;
1282 desc
.Format
= swapChainFormat
;
1283 desc
.Stereo
= FALSE
;
1284 desc
.SampleDesc
.Count
= 1;
1285 desc
.BufferCount
= 2;
1286 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1287 desc
.Scaling
= DXGI_SCALING_STRETCH
;
1288 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1289 desc
.Flags
= DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO
;
1290 if (IsYUVSwapChainFormat(swapChainFormat
)) {
1291 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO
;
1293 desc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
1296 hr
= dxgiFactoryMedia
->CreateSwapChainForCompositionSurfaceHandle(
1297 device
, mSwapChainSurfaceHandle
, &desc
, nullptr,
1298 getter_AddRefs(mVideoSwapChain
));
1301 gfxCriticalNote
<< "Failed to create video SwapChain: " << gfx::hexa(hr
)
1302 << " " << mSwapChainSize
;
1306 mSwapChainFormat
= swapChainFormat
;
1310 // TODO: Replace with YUVRangedColorSpace
1311 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1312 const gfx::YUVColorSpace aYUVColorSpace
,
1313 const gfx::ColorRange aColorRange
) {
1314 if (aYUVColorSpace
== gfx::YUVColorSpace::BT601
) {
1315 if (aColorRange
== gfx::ColorRange::FULL
) {
1316 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601
);
1318 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
);
1320 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT709
) {
1321 if (aColorRange
== gfx::ColorRange::FULL
) {
1322 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
);
1324 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
);
1326 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT2020
) {
1327 if (aColorRange
== gfx::ColorRange::FULL
) {
1328 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1330 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020
);
1332 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
);
1339 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1340 const gfx::YUVRangedColorSpace aYUVColorSpace
) {
1341 const auto info
= FromYUVRangedColorSpace(aYUVColorSpace
);
1342 return GetSourceDXGIColorSpace(info
.space
, info
.range
);
1345 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1346 MOZ_ASSERT(mRenderTextureHost
);
1349 const auto videoDevice
= mDCLayerTree
->GetVideoDevice();
1350 const auto videoContext
= mDCLayerTree
->GetVideoContext();
1351 const auto texture
= mRenderTextureHost
->AsRenderDXGITextureHost();
1353 Maybe
<DXGI_COLOR_SPACE_TYPE
> sourceColorSpace
=
1354 GetSourceDXGIColorSpace(texture
->GetYUVColorSpace());
1355 if (sourceColorSpace
.isNothing()) {
1356 gfxCriticalNote
<< "Unsupported color space";
1360 RefPtr
<ID3D11Texture2D
> texture2D
= texture
->GetD3D11Texture2DWithGL();
1362 gfxCriticalNote
<< "Failed to get D3D11Texture2D";
1366 if (!mVideoSwapChain
) {
1370 if (!mDCLayerTree
->EnsureVideoProcessor(mVideoSize
, mSwapChainSize
)) {
1371 gfxCriticalNote
<< "EnsureVideoProcessor Failed";
1375 RefPtr
<IDXGISwapChain3
> swapChain3
;
1376 mVideoSwapChain
->QueryInterface(
1377 (IDXGISwapChain3
**)getter_AddRefs(swapChain3
));
1379 gfxCriticalNote
<< "Failed to get IDXGISwapChain3";
1383 RefPtr
<ID3D11VideoContext1
> videoContext1
;
1384 videoContext
->QueryInterface(
1385 (ID3D11VideoContext1
**)getter_AddRefs(videoContext1
));
1386 if (!videoContext1
) {
1387 gfxCriticalNote
<< "Failed to get ID3D11VideoContext1";
1391 const auto videoProcessor
= mDCLayerTree
->GetVideoProcessor();
1392 const auto videoProcessorEnumerator
=
1393 mDCLayerTree
->GetVideoProcessorEnumerator();
1395 DXGI_COLOR_SPACE_TYPE inputColorSpace
= sourceColorSpace
.ref();
1396 videoContext1
->VideoProcessorSetStreamColorSpace1(videoProcessor
, 0,
1399 DXGI_COLOR_SPACE_TYPE outputColorSpace
=
1400 IsYUVSwapChainFormat(mSwapChainFormat
)
1402 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1403 hr
= swapChain3
->SetColorSpace1(outputColorSpace
);
1405 gfxCriticalNoteOnce
<< "SetColorSpace1 failed: " << gfx::hexa(hr
);
1406 RenderThread::Get()->NotifyWebRenderError(
1407 wr::WebRenderError::VIDEO_OVERLAY
);
1410 videoContext1
->VideoProcessorSetOutputColorSpace1(videoProcessor
,
1413 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc
= {};
1414 inputDesc
.ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
;
1415 inputDesc
.Texture2D
.ArraySlice
= texture
->ArrayIndex();
1417 RefPtr
<ID3D11VideoProcessorInputView
> inputView
;
1418 hr
= videoDevice
->CreateVideoProcessorInputView(
1419 texture2D
, videoProcessorEnumerator
, &inputDesc
,
1420 getter_AddRefs(inputView
));
1422 gfxCriticalNote
<< "ID3D11VideoProcessorInputView creation failed: "
1427 D3D11_VIDEO_PROCESSOR_STREAM stream
= {};
1428 stream
.Enable
= true;
1429 stream
.OutputIndex
= 0;
1430 stream
.InputFrameOrField
= 0;
1431 stream
.PastFrames
= 0;
1432 stream
.FutureFrames
= 0;
1433 stream
.pInputSurface
= inputView
.get();
1438 destRect
.right
= mSwapChainSize
.width
;
1439 destRect
.bottom
= mSwapChainSize
.height
;
1441 videoContext
->VideoProcessorSetOutputTargetRect(videoProcessor
, TRUE
,
1443 videoContext
->VideoProcessorSetStreamDestRect(videoProcessor
, 0, TRUE
,
1446 sourceRect
.left
= 0;
1448 sourceRect
.right
= mVideoSize
.width
;
1449 sourceRect
.bottom
= mVideoSize
.height
;
1450 videoContext
->VideoProcessorSetStreamSourceRect(videoProcessor
, 0, TRUE
,
1454 RefPtr
<ID3D11Texture2D
> backBuf
;
1455 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1456 (void**)getter_AddRefs(backBuf
));
1458 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc
= {};
1459 outputDesc
.ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2D
;
1460 outputDesc
.Texture2D
.MipSlice
= 0;
1462 hr
= videoDevice
->CreateVideoProcessorOutputView(
1463 backBuf
, videoProcessorEnumerator
, &outputDesc
,
1464 getter_AddRefs(mOutputView
));
1466 gfxCriticalNote
<< "ID3D11VideoProcessorOutputView creation failed: "
1472 hr
= videoContext
->VideoProcessorBlt(videoProcessor
, mOutputView
, 0, 1,
1475 gfxCriticalNote
<< "VideoProcessorBlt failed: " << gfx::hexa(hr
);
1482 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1483 mOutputView
= nullptr;
1484 mVideoSwapChain
= nullptr;
1485 mDecodeSwapChain
= nullptr;
1486 mDecodeResource
= nullptr;
1487 if (mSwapChainSurfaceHandle
) {
1488 ::CloseHandle(mSwapChainSurfaceHandle
);
1489 mSwapChainSurfaceHandle
= 0;
1493 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1494 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1497 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1498 RenderTextureHost
* texture
=
1499 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1500 RenderDcompSurfaceTextureHost
* renderTexture
=
1501 texture
? texture
->AsRenderDcompSurfaceTextureHost() : nullptr;
1502 if (!renderTexture
) {
1503 gfxCriticalNote
<< "Unsupported RenderTexture for DCSurfaceHandle: "
1504 << gfx::hexa(texture
);
1508 const auto handle
= renderTexture
->GetDcompSurfaceHandle();
1509 if (GetSurfaceHandle() == handle
) {
1513 LOG_H("AttachExternalImage, ext-image=%" PRIu64
", texture=%p, handle=%p",
1514 wr::AsUint64(aExternalImage
), renderTexture
, handle
);
1515 mDcompTextureHost
= renderTexture
;
1518 HANDLE
DCSurfaceHandle::GetSurfaceHandle() const {
1519 if (mDcompTextureHost
) {
1520 return mDcompTextureHost
->GetDcompSurfaceHandle();
1525 IDCompositionSurface
* DCSurfaceHandle::EnsureSurface() {
1526 if (auto* surface
= mDcompTextureHost
->GetSurface()) {
1530 // Texture host hasn't created the surface yet, ask it to create a new one.
1531 RefPtr
<IDCompositionDevice
> device
;
1532 HRESULT hr
= mDCLayerTree
->GetCompositionDevice()->QueryInterface(
1533 (IDCompositionDevice
**)getter_AddRefs(device
));
1536 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1541 return mDcompTextureHost
->CreateSurfaceFromDevice(device
);
1544 void DCSurfaceHandle::PresentSurfaceHandle() {
1545 LOG_H("PresentSurfaceHandle");
1546 if (IDCompositionSurface
* surface
= EnsureSurface()) {
1547 LOG_H("Set surface %p to visual", surface
);
1548 mVisual
->SetContent(surface
);
1550 mVisual
->SetContent(nullptr);
1554 DCTile::DCTile(DCLayerTree
* aDCLayerTree
) : mDCLayerTree(aDCLayerTree
) {}
1556 DCTile::~DCTile() {}
1558 bool DCTile::Initialize(int aX
, int aY
, wr::DeviceIntSize aSize
,
1559 bool aIsVirtualSurface
, bool aIsOpaque
,
1560 RefPtr
<IDCompositionVisual2
> mSurfaceVisual
) {
1561 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
1566 mIsOpaque
= aIsOpaque
;
1567 mIsVirtualSurface
= aIsVirtualSurface
;
1568 mNeedsFullDraw
= !aIsVirtualSurface
;
1570 if (aIsVirtualSurface
) {
1571 // Initially, the entire tile is considered valid, unless it is set by
1572 // the SetTileProperties method.
1575 mValidRect
.width
= aSize
.width
;
1576 mValidRect
.height
= aSize
.height
;
1579 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1580 // Create the visual and put it in the tree under the surface visual
1581 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1583 gfxCriticalNote
<< "Failed to CreateVisual for DCTile: " << gfx::hexa(hr
);
1586 mSurfaceVisual
->AddVisual(mVisual
, false, nullptr);
1587 // Position the tile relative to the surface visual
1588 mVisual
->SetOffsetX(aX
* aSize
.width
);
1589 mVisual
->SetOffsetY(aY
* aSize
.height
);
1590 // Clip the visual so it doesn't show anything until we update it
1591 D2D_RECT_F clip
= {0, 0, 0, 0};
1592 mVisual
->SetClip(clip
);
1593 // Create the underlying pixel buffer.
1594 mCompositionSurface
= CreateCompositionSurface(aSize
, aIsOpaque
);
1595 if (!mCompositionSurface
) {
1598 hr
= mVisual
->SetContent(mCompositionSurface
);
1600 gfxCriticalNote
<< "Failed to SetContent for DCTile: " << gfx::hexa(hr
);
1608 RefPtr
<IDCompositionSurface
> DCTile::CreateCompositionSurface(
1609 wr::DeviceIntSize aSize
, bool aIsOpaque
) {
1611 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1612 const auto alphaMode
=
1613 aIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1614 RefPtr
<IDCompositionSurface
> compositionSurface
;
1616 hr
= dCompDevice
->CreateSurface(aSize
.width
, aSize
.height
,
1617 DXGI_FORMAT_R8G8B8A8_UNORM
, alphaMode
,
1618 getter_AddRefs(compositionSurface
));
1620 gfxCriticalNote
<< "Failed to CreateSurface for DCTile: " << gfx::hexa(hr
);
1623 return compositionSurface
;
1626 RefPtr
<IDCompositionSurface
> DCTile::Bind(wr::DeviceIntRect aValidRect
) {
1627 if (mVisual
!= nullptr) {
1628 // Tile owns a visual, set the size of the visual to match the portion we
1629 // want to be visible.
1630 D2D_RECT_F clip_rect
;
1631 clip_rect
.left
= aValidRect
.min
.x
;
1632 clip_rect
.top
= aValidRect
.min
.y
;
1633 clip_rect
.right
= aValidRect
.max
.x
;
1634 clip_rect
.bottom
= aValidRect
.max
.y
;
1635 mVisual
->SetClip(clip_rect
);
1637 return mCompositionSurface
;
1640 GLuint
DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1641 wr::DeviceIntRect aDirtyRect
, wr::DeviceIntPoint
* aOffset
,
1642 RefPtr
<IDCompositionSurface
> aCompositionSurface
,
1643 wr::DeviceIntPoint aSurfaceOffset
) {
1644 MOZ_ASSERT(aCompositionSurface
.get());
1647 const auto gl
= GetGLContext();
1648 RefPtr
<ID3D11Texture2D
> backBuf
;
1652 update_rect
.left
= aSurfaceOffset
.x
+ aDirtyRect
.min
.x
;
1653 update_rect
.top
= aSurfaceOffset
.y
+ aDirtyRect
.min
.y
;
1654 update_rect
.right
= aSurfaceOffset
.x
+ aDirtyRect
.max
.x
;
1655 update_rect
.bottom
= aSurfaceOffset
.y
+ aDirtyRect
.max
.y
;
1656 hr
= aCompositionSurface
->BeginDraw(&update_rect
, __uuidof(ID3D11Texture2D
),
1657 (void**)getter_AddRefs(backBuf
), &offset
);
1660 LayoutDeviceIntRect rect
= widget::WinUtils::ToIntRect(update_rect
);
1662 gfxCriticalNote
<< "DCompositionSurface::BeginDraw failed: "
1663 << gfx::hexa(hr
) << " " << rect
;
1664 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW
);
1668 // DC includes the origin of the dirty / update rect in the draw offset,
1669 // undo that here since WR expects it to be an absolute offset.
1670 offset
.x
-= aDirtyRect
.min
.x
;
1671 offset
.y
-= aDirtyRect
.min
.y
;
1673 D3D11_TEXTURE2D_DESC desc
;
1674 backBuf
->GetDesc(&desc
);
1676 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1677 const auto& egl
= gle
->mEgl
;
1679 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
1681 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
1682 const EGLint attribs
[] = {LOCAL_EGL_NONE
};
1683 mEGLImage
= egl
->fCreateImage(EGL_NO_CONTEXT
, LOCAL_EGL_D3D11_TEXTURE_ANGLE
,
1686 // Get the current FBO and RBO id, so we can restore them later
1687 GLint currentFboId
, currentRboId
;
1688 gl
->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
, ¤tFboId
);
1689 gl
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, ¤tRboId
);
1691 // Create a render buffer object that is backed by the EGL image.
1692 gl
->fGenRenderbuffers(1, &mColorRBO
);
1693 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, mColorRBO
);
1694 gl
->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, mEGLImage
);
1696 // Get or create an FBO for the specified dimensions
1697 GLuint fboId
= GetOrCreateFbo(desc
.Width
, desc
.Height
);
1699 // Attach the new renderbuffer to the FBO
1700 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
1701 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
1702 LOCAL_GL_COLOR_ATTACHMENT0
,
1703 LOCAL_GL_RENDERBUFFER
, mColorRBO
);
1705 // Restore previous FBO and RBO bindings
1706 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, currentFboId
);
1707 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, currentRboId
);
1709 aOffset
->x
= offset
.x
;
1710 aOffset
->y
= offset
.y
;
1715 void DCLayerTree::DestroyEGLSurface() {
1716 const auto gl
= GetGLContext();
1719 gl
->fDeleteRenderbuffers(1, &mColorRBO
);
1724 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1725 const auto& egl
= gle
->mEgl
;
1726 egl
->fDestroyImage(mEGLImage
);
1727 mEGLImage
= EGL_NO_IMAGE
;
1733 color::ColorProfileDesc
DCLayerTree::QueryOutputColorProfile() {
1734 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
1735 // but we do need gfxPlatform::GetCMSOutputProfile().
1736 // So we steal what we need through the window:
1737 const auto outputProfileData
=
1738 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
1740 const auto qcmsProfile
= qcms_profile_from_memory(
1741 outputProfileData
.Elements(), outputProfileData
.Length());
1742 MOZ_ASSERT(qcmsProfile
);
1743 const auto release
=
1744 MakeScopeExit([&]() { qcms_profile_release(qcmsProfile
); });
1746 const auto ret
= color::ColorProfileDesc::From(*qcmsProfile
);
1747 bool print
= gfxEnv::MOZ_GL_SPEW();
1749 const auto gammaGuess
= color::GuessGamma(ret
.linearFromTf
.r
);
1751 "Display profile:\n"
1752 " Approx Gamma: %f\n"
1753 " XYZ-D65 Red : %f, %f, %f\n"
1754 " XYZ-D65 Green: %f, %f, %f\n"
1755 " XYZ-D65 Blue : %f, %f, %f\n",
1756 gammaGuess
, ret
.xyzd65FromLinearRgb
.at(0, 0),
1757 ret
.xyzd65FromLinearRgb
.at(0, 1), ret
.xyzd65FromLinearRgb
.at(0, 2),
1759 ret
.xyzd65FromLinearRgb
.at(1, 0), ret
.xyzd65FromLinearRgb
.at(1, 1),
1760 ret
.xyzd65FromLinearRgb
.at(1, 2),
1762 ret
.xyzd65FromLinearRgb
.at(2, 0), ret
.xyzd65FromLinearRgb
.at(2, 1),
1763 ret
.xyzd65FromLinearRgb
.at(2, 2));
1769 inline D2D1_MATRIX_5X4_F
to_D2D1_MATRIX_5X4_F(const color::mat4
& m
) {
1770 return D2D1_MATRIX_5X4_F
{{{
1794 ColorManagementChain
ColorManagementChain::From(
1795 IDCompositionDevice3
& dcomp
,
1796 const color::ColorProfileConversionDesc
& conv
) {
1797 auto ret
= ColorManagementChain
{};
1799 const auto Append
= [&](const RefPtr
<IDCompositionFilterEffect
>& afterLast
) {
1801 afterLast
->SetInput(0, ret
.last
, 0);
1803 ret
.last
= afterLast
;
1806 const auto MaybeAppendColorMatrix
= [&](const color::mat4
& m
) {
1807 RefPtr
<IDCompositionColorMatrixEffect
> e
;
1808 if (approx(m
, color::mat4::Identity())) return e
;
1809 dcomp
.CreateColorMatrixEffect(getter_AddRefs(e
));
1812 e
->SetMatrix(to_D2D1_MATRIX_5X4_F(m
));
1816 const auto MaybeAppendTableTransfer
= [&](const color::RgbTransferTables
& t
) {
1817 RefPtr
<IDCompositionTableTransferEffect
> e
;
1818 if (!t
.r
.size() && !t
.g
.size() && !t
.b
.size()) return e
;
1819 dcomp
.CreateTableTransferEffect(getter_AddRefs(e
));
1822 e
->SetRedTable(t
.r
.data(), t
.r
.size());
1823 e
->SetGreenTable(t
.g
.data(), t
.g
.size());
1824 e
->SetBlueTable(t
.b
.data(), t
.b
.size());
1829 ret
.srcRgbFromSrcYuv
= MaybeAppendColorMatrix(conv
.srcRgbFromSrcYuv
);
1830 ret
.srcLinearFromSrcTf
= MaybeAppendTableTransfer(conv
.srcLinearFromSrcTf
);
1831 ret
.dstLinearFromSrcLinear
=
1832 MaybeAppendColorMatrix(color::mat4(conv
.dstLinearFromSrcLinear
));
1833 ret
.dstTfFromDstLinear
= MaybeAppendTableTransfer(conv
.dstTfFromDstLinear
);
1837 ColorManagementChain::~ColorManagementChain() = default;
1840 } // namespace mozilla