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"
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/layers/HelpersD3D11.h"
27 #include "mozilla/StaticPrefs_gfx.h"
28 #include "mozilla/StaticPtr.h"
29 #include "mozilla/webrender/RenderD3D11TextureHost.h"
30 #include "mozilla/webrender/RenderDcompSurfaceTextureHost.h"
31 #include "mozilla/webrender/RenderTextureHost.h"
32 #include "mozilla/webrender/RenderThread.h"
33 #include "mozilla/WindowsVersion.h"
34 #include "mozilla/Telemetry.h"
35 #include "nsPrintfCString.h"
43 extern LazyLogModule gRenderThreadLog
;
44 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
46 #define LOG_H(msg, ...) \
47 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
48 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
50 static UINT
GetVendorId(ID3D11VideoDevice
* const aVideoDevice
) {
51 RefPtr
<IDXGIDevice
> dxgiDevice
;
52 RefPtr
<IDXGIAdapter
> adapter
;
53 aVideoDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
54 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
56 DXGI_ADAPTER_DESC adapterDesc
;
57 adapter
->GetDesc(&adapterDesc
);
59 return adapterDesc
.VendorId
;
62 // Undocumented NVIDIA VSR data
63 struct NvidiaVSRGetData_v1
{
64 UINT vsrGPUisVSRCapable
: 1; // 01/32, 1: GPU is VSR capable
65 UINT vsrOtherFieldsValid
: 1; // 02/32, 1: Other status fields are valid
66 // remaining fields are valid if vsrOtherFieldsValid is set - requires
67 // previous execution of VPBlt with SetStreamExtension for VSR enabled.
68 UINT vsrEnabled
: 1; // 03/32, 1: VSR is enabled
69 UINT vsrIsInUseForThisVP
: 1; // 04/32, 1: VSR is in use by this Video
71 UINT vsrLevel
: 3; // 05-07/32, 0-4 current level
72 UINT vsrReserved
: 21; // 32-07
75 static Result
<NvidiaVSRGetData_v1
, HRESULT
> GetNvidiaVpSuperResolutionInfo(
76 ID3D11VideoContext
* aVideoContext
, ID3D11VideoProcessor
* aVideoProcessor
) {
77 MOZ_ASSERT(aVideoContext
);
78 MOZ_ASSERT(aVideoProcessor
);
80 // Undocumented NVIDIA driver constants
81 constexpr GUID nvGUID
= {0xD43CE1B3,
84 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
86 NvidiaVSRGetData_v1 data
{};
87 HRESULT hr
= aVideoContext
->VideoProcessorGetStreamExtension(
88 aVideoProcessor
, 0, &nvGUID
, sizeof(data
), &data
);
96 static void AddProfileMarkerForNvidiaVpSuperResolutionInfo(
97 ID3D11VideoContext
* aVideoContext
, ID3D11VideoProcessor
* aVideoProcessor
) {
98 MOZ_ASSERT(profiler_thread_is_being_profiled_for_markers());
100 auto res
= GetNvidiaVpSuperResolutionInfo(aVideoContext
, aVideoProcessor
);
105 auto data
= res
.unwrap();
108 "SuperResolution VP Capable %u OtherFieldsValid %u Enabled %u InUse %u "
110 data
.vsrGPUisVSRCapable
, data
.vsrOtherFieldsValid
, data
.vsrEnabled
,
111 data
.vsrIsInUseForThisVP
, data
.vsrLevel
);
112 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS
, {}, str
);
115 static HRESULT
SetNvidiaVpSuperResolution(ID3D11VideoContext
* aVideoContext
,
116 ID3D11VideoProcessor
* aVideoProcessor
,
118 LOG("SetNvidiaVpSuperResolution() aEnable=%d", aEnable
);
120 // Undocumented NVIDIA driver constants
121 constexpr GUID nvGUID
= {0xD43CE1B3,
124 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
126 constexpr UINT nvExtensionVersion
= 0x1;
127 constexpr UINT nvExtensionMethodSuperResolution
= 0x2;
132 } streamExtensionInfo
= {nvExtensionVersion
, nvExtensionMethodSuperResolution
,
136 hr
= aVideoContext
->VideoProcessorSetStreamExtension(
137 aVideoProcessor
, 0, &nvGUID
, sizeof(streamExtensionInfo
),
138 &streamExtensionInfo
);
142 static HRESULT
SetVpSuperResolution(UINT aGpuVendorId
,
143 ID3D11VideoContext
* aVideoContext
,
144 ID3D11VideoProcessor
* aVideoProcessor
,
146 MOZ_ASSERT(aVideoContext
);
147 MOZ_ASSERT(aVideoProcessor
);
149 if (aGpuVendorId
== 0x10DE) {
150 return SetNvidiaVpSuperResolution(aVideoContext
, aVideoProcessor
, aEnable
);
155 static bool GetNvidiaRTXVideoTrueHDRSupported(
156 ID3D11VideoContext
* aVideoContext
, ID3D11VideoProcessor
* aVideoProcessor
) {
157 const GUID kNvidiaTrueHDRInterfaceGUID
= {
161 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
163 HRESULT hr
= aVideoContext
->VideoProcessorGetStreamExtension(
164 aVideoProcessor
, 0, &kNvidiaTrueHDRInterfaceGUID
, sizeof(available
),
170 bool driverSupportsTrueHdr
= (available
== 1);
171 return driverSupportsTrueHdr
;
174 static HRESULT
SetNvidiaRTXVideoTrueHDR(ID3D11VideoContext
* aVideoContext
,
175 ID3D11VideoProcessor
* aVideoProcessor
,
177 constexpr GUID kNvidiaTrueHDRInterfaceGUID
= {
181 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
182 constexpr UINT kStreamExtensionMethodTrueHDR
= 0x3;
183 const UINT TrueHDRVersion4
= 4;
189 } streamExtensionInfo
= {TrueHDRVersion4
, kStreamExtensionMethodTrueHDR
,
190 aEnable
? 1u : 0u, 0u};
191 HRESULT hr
= aVideoContext
->VideoProcessorSetStreamExtension(
192 aVideoProcessor
, 0, &kNvidiaTrueHDRInterfaceGUID
,
193 sizeof(streamExtensionInfo
), &streamExtensionInfo
);
197 static bool GetVpAutoHDRSupported(UINT aGpuVendorId
,
198 ID3D11VideoContext
* aVideoContext
,
199 ID3D11VideoProcessor
* aVideoProcessor
) {
200 MOZ_ASSERT(aVideoContext
);
201 MOZ_ASSERT(aVideoProcessor
);
203 if (aGpuVendorId
== 0x10DE) {
204 return GetNvidiaRTXVideoTrueHDRSupported(aVideoContext
, aVideoProcessor
);
209 static HRESULT
SetVpAutoHDR(UINT aGpuVendorId
,
210 ID3D11VideoContext
* aVideoContext
,
211 ID3D11VideoProcessor
* aVideoProcessor
,
213 MOZ_ASSERT(aVideoContext
);
214 MOZ_ASSERT(aVideoProcessor
);
216 if (aGpuVendorId
== 0x10DE) {
217 return SetNvidiaRTXVideoTrueHDR(aVideoContext
, aVideoProcessor
, aEnable
);
219 MOZ_ASSERT_UNREACHABLE("Unexpected to be called");
223 StaticAutoPtr
<GpuOverlayInfo
> DCLayerTree::sGpuOverlayInfo
;
226 UniquePtr
<DCLayerTree
> DCLayerTree::Create(gl::GLContext
* aGL
,
227 EGLConfig aEGLConfig
,
228 ID3D11Device
* aDevice
,
229 ID3D11DeviceContext
* aCtx
,
230 HWND aHwnd
, nsACString
& aError
) {
231 RefPtr
<IDCompositionDevice2
> dCompDevice
=
232 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
234 aError
.Assign("DCLayerTree(no device)"_ns
);
238 auto layerTree
= MakeUnique
<DCLayerTree
>(aGL
, aEGLConfig
, aDevice
, aCtx
,
240 if (!layerTree
->Initialize(aHwnd
, aError
)) {
247 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo
= nullptr; }
249 DCLayerTree::DCLayerTree(gl::GLContext
* aGL
, EGLConfig aEGLConfig
,
250 ID3D11Device
* aDevice
, ID3D11DeviceContext
* aCtx
,
251 HWND aHwnd
, IDCompositionDevice2
* aCompositionDevice
)
253 mEGLConfig(aEGLConfig
),
257 mCompositionDevice(aCompositionDevice
),
258 mDebugCounter(false),
259 mDebugVisualRedrawRegions(false),
260 mEGLImage(EGL_NO_IMAGE
),
262 mPendingCommit(false) {
263 LOG("DCLayerTree::DCLayerTree()");
266 DCLayerTree::~DCLayerTree() {
267 LOG("DCLayerTree::~DCLayerTree()");
269 ReleaseNativeCompositorResources();
272 void DCLayerTree::ReleaseNativeCompositorResources() {
273 const auto gl
= GetGLContext();
277 // Delete any cached FBO objects
278 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
279 gl
->fDeleteRenderbuffers(1, &it
->depthRboId
);
280 gl
->fDeleteFramebuffers(1, &it
->fboId
);
284 bool DCLayerTree::Initialize(HWND aHwnd
, nsACString
& aError
) {
287 RefPtr
<IDCompositionDesktopDevice
> desktopDevice
;
288 hr
= mCompositionDevice
->QueryInterface(
289 (IDCompositionDesktopDevice
**)getter_AddRefs(desktopDevice
));
291 aError
.Assign(nsPrintfCString(
292 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr
));
296 hr
= desktopDevice
->CreateTargetForHwnd(aHwnd
, TRUE
,
297 getter_AddRefs(mCompositionTarget
));
299 aError
.Assign(nsPrintfCString(
300 "DCLayerTree(create DCompositionTarget failed %lx)", hr
));
304 hr
= mCompositionDevice
->CreateVisual(getter_AddRefs(mRootVisual
));
306 aError
.Assign(nsPrintfCString(
307 "DCLayerTree(create root DCompositionVisual failed %lx)", hr
));
312 mCompositionDevice
->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual
));
314 aError
.Assign(nsPrintfCString(
315 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr
));
319 if (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin() ||
320 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) {
321 if (!InitializeVideoOverlaySupport()) {
322 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY
);
325 if (!sGpuOverlayInfo
) {
326 // Set default if sGpuOverlayInfo was not set.
327 sGpuOverlayInfo
= new GpuOverlayInfo();
330 // Initialize SwapChainInfo
331 SupportsSwapChainTearing();
333 mCompositionTarget
->SetRoot(mRootVisual
);
334 // Set interporation mode to nearest, to ensure 1:1 sampling.
335 // By default, a visual inherits the interpolation mode of the parent visual.
336 // If no visuals set the interpolation mode, the default for the entire visual
337 // tree is nearest neighbor interpolation.
338 mRootVisual
->SetBitmapInterpolationMode(
339 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
343 bool FlagsSupportsOverlays(UINT flags
) {
344 return (flags
& (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
|
345 DXGI_OVERLAY_SUPPORT_FLAG_SCALING
));
348 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
349 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat
,
350 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace
,
351 RefPtr
<IDXGIOutput
> aOutput
,
352 RefPtr
<ID3D11Device
> aD3d11Device
) {
353 UINT colorSpaceSupportFlags
= 0;
354 RefPtr
<IDXGIOutput4
> output4
;
356 if (FAILED(aOutput
->QueryInterface(__uuidof(IDXGIOutput4
),
357 getter_AddRefs(output4
)))) {
361 if (FAILED(output4
->CheckOverlayColorSpaceSupport(
362 aDxgiFormat
, aDxgiColorSpace
, aD3d11Device
,
363 &colorSpaceSupportFlags
))) {
367 return (colorSpaceSupportFlags
&
368 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT
);
371 bool DCLayerTree::InitializeVideoOverlaySupport() {
372 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
376 hr
= mDevice
->QueryInterface(
377 (ID3D11VideoDevice
**)getter_AddRefs(mVideoDevice
));
379 gfxCriticalNote
<< "Failed to get D3D11VideoDevice: " << gfx::hexa(hr
);
384 mCtx
->QueryInterface((ID3D11VideoContext
**)getter_AddRefs(mVideoContext
));
386 gfxCriticalNote
<< "Failed to get D3D11VideoContext: " << gfx::hexa(hr
);
390 if (sGpuOverlayInfo
) {
394 UniquePtr
<GpuOverlayInfo
> info
= MakeUnique
<GpuOverlayInfo
>();
396 RefPtr
<IDXGIDevice
> dxgiDevice
;
397 RefPtr
<IDXGIAdapter
> adapter
;
398 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
399 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
403 RefPtr
<IDXGIOutput
> output
;
404 if (FAILED(adapter
->EnumOutputs(i
++, getter_AddRefs(output
)))) {
407 RefPtr
<IDXGIOutput3
> output3
;
408 if (FAILED(output
->QueryInterface(__uuidof(IDXGIOutput3
),
409 getter_AddRefs(output3
)))) {
413 output3
->CheckOverlaySupport(DXGI_FORMAT_NV12
, mDevice
,
414 &info
->mNv12OverlaySupportFlags
);
415 output3
->CheckOverlaySupport(DXGI_FORMAT_YUY2
, mDevice
,
416 &info
->mYuy2OverlaySupportFlags
);
417 output3
->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM
, mDevice
,
418 &info
->mBgra8OverlaySupportFlags
);
419 output3
->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM
, mDevice
,
420 &info
->mRgb10a2OverlaySupportFlags
);
422 if (FlagsSupportsOverlays(info
->mNv12OverlaySupportFlags
)) {
423 // NV12 format is preferred if it's supported.
424 info
->mOverlayFormatUsed
= DXGI_FORMAT_NV12
;
425 info
->mSupportsHardwareOverlays
= true;
428 if (!info
->mSupportsHardwareOverlays
&&
429 FlagsSupportsOverlays(info
->mYuy2OverlaySupportFlags
)) {
430 // If NV12 isn't supported, fallback to YUY2 if it's supported.
431 info
->mOverlayFormatUsed
= DXGI_FORMAT_YUY2
;
432 info
->mSupportsHardwareOverlays
= true;
435 // RGB10A2 overlay is used for displaying HDR content. In Intel's
436 // platform, RGB10A2 overlay is enabled only when
437 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
438 if (FlagsSupportsOverlays(info
->mRgb10a2OverlaySupportFlags
)) {
439 if (!CheckOverlayColorSpaceSupport(
440 DXGI_FORMAT_R10G10B10A2_UNORM
,
441 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
, output
, mDevice
))
442 info
->mRgb10a2OverlaySupportFlags
= 0;
445 // Early out after the first output that reports overlay support. All
446 // outputs are expected to report the same overlay support according to
447 // Microsoft's WDDM documentation:
448 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
449 if (info
->mSupportsHardwareOverlays
) {
454 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
455 info
->mOverlayFormatUsed
= DXGI_FORMAT_B8G8R8A8_UNORM
;
456 info
->mSupportsHardwareOverlays
= false;
459 info
->mSupportsOverlays
= info
->mSupportsHardwareOverlays
;
461 // Check VpSuperResolution and VpAutoHDR support.
462 const auto size
= gfx::IntSize(100, 100);
463 if (EnsureVideoProcessor(size
, size
)) {
464 const UINT vendorId
= GetVendorId(mVideoDevice
);
465 if (vendorId
== 0x10DE) {
466 auto res
= GetNvidiaVpSuperResolutionInfo(mVideoContext
, mVideoProcessor
);
467 if (res
.isOk() && res
.unwrap().vsrGPUisVSRCapable
) {
468 info
->mSupportsVpSuperResolution
= true;
472 const bool driverSupportVpAutoHDR
=
473 GetVpAutoHDRSupported(vendorId
, mVideoContext
, mVideoProcessor
);
474 if (driverSupportVpAutoHDR
) {
475 info
->mSupportsVpAutoHDR
= true;
479 // Note: "UniquePtr::release" here is saying "release your ownership stake
480 // on your pointer, so that our StaticAutoPtr can take over ownership".
481 // (StaticAutoPtr doesn't have a move constructor that could directly steal
482 // the contents of a UniquePtr via std::move().)
483 sGpuOverlayInfo
= info
.release();
485 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
486 gpuParent
->NotifyOverlayInfo(GetOverlayInfo());
492 DCSurface
* DCLayerTree::GetSurface(wr::NativeSurfaceId aId
) const {
493 auto surface_it
= mDCSurfaces
.find(aId
);
494 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
495 return surface_it
->second
.get();
498 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1
* aSwapChain
) {
499 LOG("DCLayerTree::SetDefaultSwapChain()");
501 mRootVisual
->AddVisual(mDefaultSwapChainVisual
, TRUE
, nullptr);
502 mDefaultSwapChainVisual
->SetContent(aSwapChain
);
503 // Default SwapChain's visual does not need linear interporation.
504 mDefaultSwapChainVisual
->SetBitmapInterpolationMode(
505 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
506 mPendingCommit
= true;
509 void DCLayerTree::MaybeUpdateDebug() {
510 bool updated
= false;
511 updated
|= MaybeUpdateDebugCounter();
512 updated
|= MaybeUpdateDebugVisualRedrawRegions();
514 mPendingCommit
= true;
518 void DCLayerTree::MaybeCommit() {
519 if (!mPendingCommit
) {
522 mCompositionDevice
->Commit();
525 void DCLayerTree::WaitForCommitCompletion() {
526 // To ensure that swapchain layers have presented to the screen
527 // for capture, call present twice. This is less than ideal, but
528 // I'm not sure if there is a better way to ensure this syncs
529 // correctly that works on both Win10/11. Even though this can
530 // be slower than necessary, it's only used by the reftest
531 // screenshotting code, so isn't particularly perf sensitive.
532 for (auto it
= mDCSurfaces
.begin(); it
!= mDCSurfaces
.end(); it
++) {
533 auto* surface
= it
->second
->AsDCSwapChain();
540 mCompositionDevice
->WaitForCommitCompletion();
543 void DCLayerTree::DisableNativeCompositor() {
544 MOZ_ASSERT(mCurrentSurface
.isNothing());
545 MOZ_ASSERT(mCurrentLayers
.empty());
547 ReleaseNativeCompositorResources();
549 mRootVisual
->RemoveAllVisuals();
552 bool DCLayerTree::MaybeUpdateDebugCounter() {
553 bool debugCounter
= StaticPrefs::gfx_webrender_debug_dcomp_counter();
554 if (mDebugCounter
== debugCounter
) {
558 RefPtr
<IDCompositionDeviceDebug
> debugDevice
;
559 HRESULT hr
= mCompositionDevice
->QueryInterface(
560 (IDCompositionDeviceDebug
**)getter_AddRefs(debugDevice
));
566 debugDevice
->EnableDebugCounters();
568 debugDevice
->DisableDebugCounters();
571 mDebugCounter
= debugCounter
;
575 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
576 bool debugVisualRedrawRegions
=
577 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
578 if (mDebugVisualRedrawRegions
== debugVisualRedrawRegions
) {
582 RefPtr
<IDCompositionVisualDebug
> visualDebug
;
583 HRESULT hr
= mRootVisual
->QueryInterface(
584 (IDCompositionVisualDebug
**)getter_AddRefs(visualDebug
));
589 if (debugVisualRedrawRegions
) {
590 visualDebug
->EnableRedrawRegions();
592 visualDebug
->DisableRedrawRegions();
595 mDebugVisualRedrawRegions
= debugVisualRedrawRegions
;
599 void DCLayerTree::CompositorBeginFrame() {
601 mUsedOverlayTypesInFrame
= DCompOverlayTypes::NO_OVERLAY
;
604 void DCLayerTree::CompositorEndFrame() {
605 auto start
= TimeStamp::Now();
606 // Check if the visual tree of surfaces is the same as last frame.
607 bool same
= mPrevLayers
== mCurrentLayers
;
610 // If not, we need to rebuild the visual tree. Note that addition or
611 // removal of tiles no longer needs to rebuild the main visual tree
612 // here, since they are added as children of the surface visual.
613 mRootVisual
->RemoveAllVisuals();
616 for (auto it
= mCurrentLayers
.begin(); it
!= mCurrentLayers
.end(); ++it
) {
617 auto surface_it
= mDCSurfaces
.find(*it
);
618 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
619 const auto surface
= surface_it
->second
.get();
620 // Ensure surface is trimmed to updated tile valid rects
621 surface
->UpdateAllocatedRect();
623 // Add surfaces in z-order they were added to the scene.
624 const auto visual
= surface
->GetVisual();
625 mRootVisual
->AddVisual(visual
, false, nullptr);
629 mPrevLayers
.swap(mCurrentLayers
);
630 mCurrentLayers
.clear();
632 mCompositionDevice
->Commit();
634 auto end
= TimeStamp::Now();
635 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
636 (end
- start
).ToMilliseconds() * 10.);
638 // Remove any framebuffers that haven't been
639 // used in the last 60 frames.
641 // This should use nsTArray::RemoveElementsBy once
642 // CachedFrameBuffer is able to properly destroy
643 // itself in the destructor.
644 const auto gl
= GetGLContext();
645 for (uint32_t i
= 0, len
= mFrameBuffers
.Length(); i
< len
; ++i
) {
646 auto& fb
= mFrameBuffers
[i
];
647 if ((mCurrentFrame
- fb
.lastFrameUsed
) > 60) {
648 gl
->fDeleteRenderbuffers(1, &fb
.depthRboId
);
649 gl
->fDeleteFramebuffers(1, &fb
.fboId
);
650 mFrameBuffers
.UnorderedRemoveElementAt(i
);
651 --i
; // Examine the element again, if necessary.
656 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
660 // Disable video overlay if mCompositionDevice->Commit() with video overlay is
661 // too slow. It drops fps.
663 const auto maxCommitWaitDurationMs
= 20;
664 const auto maxSlowCommitCount
= 5;
665 const auto commitDurationMs
=
666 static_cast<uint32_t>((end
- start
).ToMilliseconds());
668 nsPrintfCString
marker("CommitWait overlay %u %ums ",
669 (uint8_t)mUsedOverlayTypesInFrame
, commitDurationMs
);
670 PROFILER_MARKER_TEXT("CommitWait", GRAPHICS
, {}, marker
);
672 if (mUsedOverlayTypesInFrame
!= DCompOverlayTypes::NO_OVERLAY
&&
673 commitDurationMs
> maxCommitWaitDurationMs
) {
676 mSlowCommitCount
= 0;
679 if (mSlowCommitCount
<= maxSlowCommitCount
) {
683 for (auto it
= mDCSurfaces
.begin(); it
!= mDCSurfaces
.end(); it
++) {
684 auto* surfaceVideo
= it
->second
->AsDCSurfaceVideo();
686 surfaceVideo
->DisableVideoOverlay();
690 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
691 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
693 nsPrintfCString
marker("Sw video swapchain present is slow");
694 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS
, {}, marker
);
696 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::HARDWARE_DECODED_VIDEO
) {
697 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
699 nsPrintfCString
marker("Hw video swapchain present is slow");
700 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS
, {}, marker
);
704 void DCLayerTree::BindSwapChain(wr::NativeSurfaceId aId
) {
705 auto surface
= GetSurface(aId
);
706 surface
->AsDCSwapChain()->Bind();
709 void DCLayerTree::PresentSwapChain(wr::NativeSurfaceId aId
) {
710 auto surface
= GetSurface(aId
);
711 surface
->AsDCSwapChain()->Present();
714 void DCLayerTree::Bind(wr::NativeTileId aId
, wr::DeviceIntPoint
* aOffset
,
715 uint32_t* aFboId
, wr::DeviceIntRect aDirtyRect
,
716 wr::DeviceIntRect aValidRect
) {
717 auto surface
= GetSurface(aId
.surface_id
);
718 auto tile
= surface
->GetTile(aId
.x
, aId
.y
);
719 wr::DeviceIntPoint targetOffset
{0, 0};
721 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
722 // IDCompositionVirtualSurface owned by the DCSurface.
723 RefPtr
<IDCompositionSurface
> compositionSurface
;
724 if (surface
->mIsVirtualSurface
) {
725 gfx::IntRect
validRect(aValidRect
.min
.x
, aValidRect
.min
.y
,
726 aValidRect
.width(), aValidRect
.height());
727 if (!tile
->mValidRect
.IsEqualEdges(validRect
)) {
728 tile
->mValidRect
= validRect
;
729 surface
->DirtyAllocatedRect();
731 wr::DeviceIntSize tileSize
= surface
->GetTileSize();
732 compositionSurface
= surface
->GetCompositionSurface();
733 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
734 targetOffset
.x
= virtualOffset
.x
+ tileSize
.width
* aId
.x
;
735 targetOffset
.y
= virtualOffset
.y
+ tileSize
.height
* aId
.y
;
737 compositionSurface
= tile
->Bind(aValidRect
);
740 if (tile
->mNeedsFullDraw
) {
741 // dcomp requires that the first BeginDraw on a non-virtual surface is the
742 // full size of the pixel buffer.
743 auto tileSize
= surface
->GetTileSize();
744 aDirtyRect
.min
.x
= 0;
745 aDirtyRect
.min
.y
= 0;
746 aDirtyRect
.max
.x
= tileSize
.width
;
747 aDirtyRect
.max
.y
= tileSize
.height
;
748 tile
->mNeedsFullDraw
= false;
751 *aFboId
= CreateEGLSurfaceForCompositionSurface(
752 aDirtyRect
, aOffset
, compositionSurface
, targetOffset
);
753 mCurrentSurface
= Some(compositionSurface
);
756 void DCLayerTree::Unbind() {
757 if (mCurrentSurface
.isNothing()) {
761 RefPtr
<IDCompositionSurface
> surface
= mCurrentSurface
.ref();
765 mCurrentSurface
= Nothing();
768 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId
,
769 wr::DeviceIntPoint aVirtualOffset
,
770 wr::DeviceIntSize aTileSize
, bool aIsOpaque
) {
771 auto it
= mDCSurfaces
.find(aId
);
772 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
773 if (it
!= mDCSurfaces
.end()) {
774 // DCSurface already exists.
778 // Tile size needs to be positive.
779 if (aTileSize
.width
<= 0 || aTileSize
.height
<= 0) {
780 gfxCriticalNote
<< "TileSize is not positive aId: " << wr::AsUint64(aId
)
781 << " aTileSize(" << aTileSize
.width
<< ","
782 << aTileSize
.height
<< ")";
785 bool isVirtualSurface
=
786 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
787 auto surface
= MakeUnique
<DCSurface
>(aTileSize
, aVirtualOffset
,
788 isVirtualSurface
, aIsOpaque
, this);
789 if (!surface
->Initialize()) {
790 gfxCriticalNote
<< "Failed to initialize DCSurface: " << wr::AsUint64(aId
);
794 mDCSurfaces
[aId
] = std::move(surface
);
797 void DCLayerTree::CreateSwapChainSurface(wr::NativeSurfaceId aId
,
798 wr::DeviceIntSize aSize
,
800 auto it
= mDCSurfaces
.find(aId
);
801 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
803 auto surface
= MakeUnique
<DCSwapChain
>(aSize
, aIsOpaque
, this);
804 if (!surface
->Initialize()) {
805 gfxCriticalNote
<< "Failed to initialize DCSwapChain: "
806 << wr::AsUint64(aId
);
810 mDCSurfaces
[aId
] = std::move(surface
);
813 void DCLayerTree::ResizeSwapChainSurface(wr::NativeSurfaceId aId
,
814 wr::DeviceIntSize aSize
) {
815 auto it
= mDCSurfaces
.find(aId
);
816 MOZ_RELEASE_ASSERT(it
!= mDCSurfaces
.end());
817 auto surface
= it
->second
.get();
819 surface
->AsDCSwapChain()->Resize(aSize
);
822 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId
,
824 auto it
= mDCSurfaces
.find(aId
);
825 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
827 auto surface
= MakeUnique
<DCExternalSurfaceWrapper
>(aIsOpaque
, this);
828 if (!surface
->Initialize()) {
829 gfxCriticalNote
<< "Failed to initialize DCExternalSurfaceWrapper: "
830 << wr::AsUint64(aId
);
834 mDCSurfaces
[aId
] = std::move(surface
);
837 void DCLayerTree::DestroySurface(NativeSurfaceId aId
) {
838 auto surface_it
= mDCSurfaces
.find(aId
);
839 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
840 auto surface
= surface_it
->second
.get();
842 mRootVisual
->RemoveVisual(surface
->GetVisual());
843 mDCSurfaces
.erase(surface_it
);
846 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
847 auto surface
= GetSurface(aId
);
848 surface
->CreateTile(aX
, aY
);
851 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
852 auto surface
= GetSurface(aId
);
853 surface
->DestroyTile(aX
, aY
);
856 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId
,
857 wr::ExternalImageId aExternalImage
) {
858 auto surface_it
= mDCSurfaces
.find(aId
);
859 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
860 surface_it
->second
->AttachExternalImage(aExternalImage
);
863 void DCExternalSurfaceWrapper::AttachExternalImage(
864 wr::ExternalImageId aExternalImage
) {
865 if (auto* surface
= EnsureSurfaceForExternalImage(aExternalImage
)) {
866 surface
->AttachExternalImage(aExternalImage
);
872 template <class FromT
>
873 [[nodiscard
]] static inline RefPtr
<ToT
> From(FromT
* const from
) {
875 (void)from
->QueryInterface(static_cast<ToT
**>(getter_AddRefs(to
)));
880 DCSurface
* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
881 wr::ExternalImageId aExternalImage
) {
883 return mSurface
.get();
886 // Create a new surface based on the texture type.
887 RenderTextureHost
* texture
=
888 RenderThread::Get()->GetRenderTexture(aExternalImage
);
889 if (texture
&& texture
->AsRenderDXGITextureHost()) {
890 mSurface
.reset(new DCSurfaceVideo(mIsOpaque
, mDCLayerTree
));
891 if (!mSurface
->Initialize()) {
892 gfxCriticalNote
<< "Failed to initialize DCSurfaceVideo: "
893 << wr::AsUint64(aExternalImage
);
896 } else if (texture
&& texture
->AsRenderDcompSurfaceTextureHost()) {
897 mSurface
.reset(new DCSurfaceHandle(mIsOpaque
, mDCLayerTree
));
898 if (!mSurface
->Initialize()) {
899 gfxCriticalNote
<< "Failed to initialize DCSurfaceHandle: "
900 << wr::AsUint64(aExternalImage
);
905 gfxCriticalNote
<< "Failed to create a surface for external image: "
906 << gfx::hexa(texture
);
910 // Add surface's visual which will contain video data to our root visual.
911 const auto surfaceVisual
= mSurface
->GetVisual();
912 mVisual
->AddVisual(surfaceVisual
, true, nullptr);
915 // Apply color management.
918 if (!StaticPrefs::gfx_webrender_dcomp_color_manage_with_filters()) return;
920 const auto cmsMode
= GfxColorManagementMode();
921 if (cmsMode
== CMSMode::Off
) return;
923 const auto dcomp
= mDCLayerTree
->GetCompositionDevice();
924 const auto dcomp3
= QI
<IDCompositionDevice3
>::From(dcomp
);
927 "No IDCompositionDevice3, cannot use dcomp for color management.");
933 const auto cspace
= [&]() {
934 const auto rangedCspace
= texture
->GetYUVColorSpace();
935 const auto info
= FromYUVRangedColorSpace(rangedCspace
);
936 auto ret
= ToColorSpace2(info
.space
);
937 if (ret
== gfx::ColorSpace2::Display
&& cmsMode
== CMSMode::All
) {
938 ret
= gfx::ColorSpace2::SRGB
;
943 const bool rec709GammaAsSrgb
=
944 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
945 const bool rec2020GammaAsRec709
=
946 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
948 auto cspaceDesc
= color::ColorspaceDesc
{};
950 case gfx::ColorSpace2::Display
:
951 return; // No color management needed!
952 case gfx::ColorSpace2::SRGB
:
953 cspaceDesc
.chrom
= color::Chromaticities::Srgb();
954 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
957 case gfx::ColorSpace2::DISPLAY_P3
:
958 cspaceDesc
.chrom
= color::Chromaticities::DisplayP3();
959 cspaceDesc
.tf
= color::PiecewiseGammaDesc::DisplayP3();
962 case gfx::ColorSpace2::BT601_525
:
963 cspaceDesc
.chrom
= color::Chromaticities::Rec601_525_Ntsc();
964 if (rec709GammaAsSrgb
) {
965 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
967 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
971 case gfx::ColorSpace2::BT709
:
972 cspaceDesc
.chrom
= color::Chromaticities::Rec709();
973 if (rec709GammaAsSrgb
) {
974 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
976 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
980 case gfx::ColorSpace2::BT2020
:
981 cspaceDesc
.chrom
= color::Chromaticities::Rec2020();
982 if (rec2020GammaAsRec709
&& rec709GammaAsSrgb
) {
983 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
984 } else if (rec2020GammaAsRec709
) {
985 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
987 // Just Rec709 with slightly more precision.
988 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec2020_12bit();
993 const auto cprofileIn
= color::ColorProfileDesc::From(cspaceDesc
);
994 auto cprofileOut
= mDCLayerTree
->OutputColorProfile();
995 bool pretendSrgb
= true;
997 cprofileOut
= color::ColorProfileDesc::From(color::ColorspaceDesc
{
998 .chrom
= color::Chromaticities::Srgb(),
999 .tf
= color::PiecewiseGammaDesc::Srgb(),
1002 const auto conversion
= color::ColorProfileConversionDesc::From({
1009 auto chain
= ColorManagementChain::From(*dcomp3
, conversion
);
1010 mCManageChain
= Some(chain
);
1012 surfaceVisual
->SetEffect(mCManageChain
->last
.get());
1015 return mSurface
.get();
1018 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix
& aTransform
) {
1019 MOZ_ASSERT(mSurface
);
1020 if (auto* surface
= mSurface
->AsDCSurfaceVideo()) {
1021 if (surface
->CalculateSwapChainSize(aTransform
)) {
1022 surface
->PresentVideo();
1024 } else if (auto* surface
= mSurface
->AsDCSurfaceHandle()) {
1025 surface
->PresentSurfaceHandle();
1029 template <typename T
>
1030 static inline D2D1_RECT_F
D2DRect(const T
& aRect
) {
1031 return D2D1::RectF(aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost());
1034 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const gfx::Matrix
& aTransform
) {
1035 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
, aTransform
._21
,
1036 aTransform
._22
, aTransform
._31
, aTransform
._32
);
1039 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId
,
1040 const wr::CompositorSurfaceTransform
& aTransform
,
1041 wr::DeviceIntRect aClipRect
,
1042 wr::ImageRendering aImageRendering
) {
1043 auto it
= mDCSurfaces
.find(aId
);
1044 MOZ_RELEASE_ASSERT(it
!= mDCSurfaces
.end());
1045 const auto surface
= it
->second
.get();
1046 const auto visual
= surface
->GetVisual();
1048 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
1050 float sx
= aTransform
.scale
.x
;
1051 float sy
= aTransform
.scale
.y
;
1052 float tx
= aTransform
.offset
.x
;
1053 float ty
= aTransform
.offset
.y
;
1054 gfx::Matrix
transform(sx
, 0.0, 0.0, sy
, tx
, ty
);
1056 surface
->PresentExternalSurface(transform
);
1058 transform
.PreTranslate(-virtualOffset
.x
, -virtualOffset
.y
);
1060 // The DirectComposition API applies clipping *before* any
1061 // transforms/offset, whereas we want the clip applied after. Right now, we
1062 // only support rectilinear transforms, and then we transform our clip into
1063 // pre-transform coordinate space for it to be applied there.
1064 // DirectComposition does have an option for pre-transform clipping, if you
1065 // create an explicit IDCompositionEffectGroup object and set a 3D transform
1066 // on that. I suspect that will perform worse though, so we should only do
1067 // that for complex transforms (which are never provided right now).
1068 MOZ_ASSERT(transform
.IsRectilinear());
1069 gfx::Rect clip
= transform
.Inverse().TransformBounds(gfx::Rect(
1070 aClipRect
.min
.x
, aClipRect
.min
.y
, aClipRect
.width(), aClipRect
.height()));
1071 // Set the clip rect - converting from world space to the pre-offset space
1072 // that DC requires for rectangle clips.
1073 visual
->SetClip(D2DRect(clip
));
1075 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
1076 // the D3D API level (unless we QI to IDCompositionVisual3, which might
1077 // not be available?).
1078 // Should we assert here, or restrict at the WR API level.
1079 visual
->SetTransform(D2DMatrix(transform
));
1081 if (aImageRendering
== wr::ImageRendering::Auto
) {
1082 visual
->SetBitmapInterpolationMode(
1083 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR
);
1085 visual
->SetBitmapInterpolationMode(
1086 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
1089 mCurrentLayers
.push_back(aId
);
1092 GLuint
DCLayerTree::GetOrCreateFbo(int aWidth
, int aHeight
) {
1093 const auto gl
= GetGLContext();
1096 // Check if we have a cached FBO with matching dimensions
1097 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
1098 if (it
->width
== aWidth
&& it
->height
== aHeight
) {
1100 it
->lastFrameUsed
= mCurrentFrame
;
1105 // If not, create a new FBO with attached depth buffer
1107 // Create the depth buffer
1109 gl
->fGenRenderbuffers(1, &depthRboId
);
1110 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, depthRboId
);
1111 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, LOCAL_GL_DEPTH_COMPONENT24
,
1114 // Create the framebuffer and attach the depth buffer to it
1115 gl
->fGenFramebuffers(1, &fboId
);
1116 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
1117 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
1118 LOCAL_GL_DEPTH_ATTACHMENT
,
1119 LOCAL_GL_RENDERBUFFER
, depthRboId
);
1121 // Store this in the cache for future calls.
1122 // TODO(gw): Maybe we should periodically scan this list and remove old
1124 // haven't been used for some time?
1125 DCLayerTree::CachedFrameBuffer frame_buffer_info
;
1126 frame_buffer_info
.width
= aWidth
;
1127 frame_buffer_info
.height
= aHeight
;
1128 frame_buffer_info
.fboId
= fboId
;
1129 frame_buffer_info
.depthRboId
= depthRboId
;
1130 frame_buffer_info
.lastFrameUsed
= mCurrentFrame
;
1131 mFrameBuffers
.AppendElement(frame_buffer_info
);
1137 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize
& aInputSize
,
1138 const gfx::IntSize
& aOutputSize
) {
1141 if (!mVideoDevice
|| !mVideoContext
) {
1145 if (mVideoProcessor
&& (aInputSize
<= mVideoInputSize
) &&
1146 (aOutputSize
<= mVideoOutputSize
)) {
1150 mVideoProcessor
= nullptr;
1151 mVideoProcessorEnumerator
= nullptr;
1153 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc
= {};
1154 desc
.InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE
;
1155 desc
.InputFrameRate
.Numerator
= 60;
1156 desc
.InputFrameRate
.Denominator
= 1;
1157 desc
.InputWidth
= aInputSize
.width
;
1158 desc
.InputHeight
= aInputSize
.height
;
1159 desc
.OutputFrameRate
.Numerator
= 60;
1160 desc
.OutputFrameRate
.Denominator
= 1;
1161 desc
.OutputWidth
= aOutputSize
.width
;
1162 desc
.OutputHeight
= aOutputSize
.height
;
1163 desc
.Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
;
1165 hr
= mVideoDevice
->CreateVideoProcessorEnumerator(
1166 &desc
, getter_AddRefs(mVideoProcessorEnumerator
));
1168 gfxCriticalNote
<< "Failed to create VideoProcessorEnumerator: "
1173 hr
= mVideoDevice
->CreateVideoProcessor(mVideoProcessorEnumerator
, 0,
1174 getter_AddRefs(mVideoProcessor
));
1176 mVideoProcessor
= nullptr;
1177 mVideoProcessorEnumerator
= nullptr;
1178 gfxCriticalNote
<< "Failed to create VideoProcessor: " << gfx::hexa(hr
);
1182 // Reduce power cosumption
1183 // By default, the driver might perform certain processing tasks
1185 mVideoContext
->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor
, 0,
1188 mVideoInputSize
= aInputSize
;
1189 mVideoOutputSize
= aOutputSize
;
1194 bool DCLayerTree::SupportsHardwareOverlays() {
1195 return sGpuOverlayInfo
->mSupportsHardwareOverlays
;
1198 bool DCLayerTree::SupportsSwapChainTearing() {
1199 RefPtr
<ID3D11Device
> device
= mDevice
;
1200 static const bool supported
= [device
] {
1201 RefPtr
<IDXGIDevice
> dxgiDevice
;
1202 RefPtr
<IDXGIAdapter
> adapter
;
1203 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1204 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1206 RefPtr
<IDXGIFactory5
> dxgiFactory
;
1207 HRESULT hr
= adapter
->GetParent(
1208 IID_PPV_ARGS((IDXGIFactory5
**)getter_AddRefs(dxgiFactory
)));
1213 BOOL presentAllowTearing
= FALSE
;
1214 hr
= dxgiFactory
->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING
,
1215 &presentAllowTearing
,
1216 sizeof(presentAllowTearing
));
1221 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
1222 gpuParent
->NotifySwapChainInfo(
1223 layers::SwapChainInfo(!!presentAllowTearing
));
1224 } else if (XRE_IsParentProcess()) {
1225 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1227 return !!presentAllowTearing
;
1232 DXGI_FORMAT
DCLayerTree::GetOverlayFormatForSDR() {
1233 return sGpuOverlayInfo
->mOverlayFormatUsed
;
1236 static layers::OverlaySupportType
FlagsToOverlaySupportType(
1237 UINT aFlags
, bool aSoftwareOverlaySupported
) {
1238 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_SCALING
) {
1239 return layers::OverlaySupportType::Scaling
;
1241 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
) {
1242 return layers::OverlaySupportType::Direct
;
1244 if (aSoftwareOverlaySupported
) {
1245 return layers::OverlaySupportType::Software
;
1247 return layers::OverlaySupportType::None
;
1250 layers::OverlayInfo
DCLayerTree::GetOverlayInfo() {
1251 layers::OverlayInfo info
;
1253 info
.mSupportsOverlays
= sGpuOverlayInfo
->mSupportsHardwareOverlays
;
1255 FlagsToOverlaySupportType(sGpuOverlayInfo
->mNv12OverlaySupportFlags
,
1256 /* aSoftwareOverlaySupported */ false);
1258 FlagsToOverlaySupportType(sGpuOverlayInfo
->mYuy2OverlaySupportFlags
,
1259 /* aSoftwareOverlaySupported */ false);
1260 info
.mBgra8Overlay
=
1261 FlagsToOverlaySupportType(sGpuOverlayInfo
->mBgra8OverlaySupportFlags
,
1262 /* aSoftwareOverlaySupported */ true);
1263 info
.mRgb10a2Overlay
=
1264 FlagsToOverlaySupportType(sGpuOverlayInfo
->mRgb10a2OverlaySupportFlags
,
1265 /* aSoftwareOverlaySupported */ false);
1267 info
.mSupportsVpSuperResolution
= sGpuOverlayInfo
->mSupportsVpSuperResolution
;
1268 info
.mSupportsVpAutoHDR
= sGpuOverlayInfo
->mSupportsVpAutoHDR
;
1273 void DCLayerTree::SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes
) {
1274 mUsedOverlayTypesInFrame
|= aTypes
;
1277 DCSurface::DCSurface(wr::DeviceIntSize aTileSize
,
1278 wr::DeviceIntPoint aVirtualOffset
, bool aIsVirtualSurface
,
1279 bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1280 : mIsVirtualSurface(aIsVirtualSurface
),
1281 mDCLayerTree(aDCLayerTree
),
1282 mTileSize(aTileSize
),
1283 mIsOpaque(aIsOpaque
),
1284 mAllocatedRectDirty(true),
1285 mVirtualOffset(aVirtualOffset
) {}
1287 DCSurface::~DCSurface() {}
1289 bool DCSurface::Initialize() {
1290 // Create a visual for tiles to attach to, whether virtual or not.
1292 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1293 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1295 gfxCriticalNote
<< "Failed to create DCompositionVisual: " << gfx::hexa(hr
);
1299 // If virtual surface is enabled, create and attach to visual, in this case
1300 // the tiles won't own visuals or surfaces.
1301 if (mIsVirtualSurface
) {
1302 DXGI_ALPHA_MODE alpha_mode
=
1303 mIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1305 hr
= dCompDevice
->CreateVirtualSurface(
1306 VIRTUAL_SURFACE_SIZE
, VIRTUAL_SURFACE_SIZE
, DXGI_FORMAT_R8G8B8A8_UNORM
,
1307 alpha_mode
, getter_AddRefs(mVirtualSurface
));
1308 MOZ_ASSERT(SUCCEEDED(hr
));
1310 // Bind the surface memory to this visual
1311 hr
= mVisual
->SetContent(mVirtualSurface
);
1312 MOZ_ASSERT(SUCCEEDED(hr
));
1318 void DCSurface::CreateTile(int32_t aX
, int32_t aY
) {
1319 TileKey
key(aX
, aY
);
1320 MOZ_RELEASE_ASSERT(mDCTiles
.find(key
) == mDCTiles
.end());
1322 auto tile
= MakeUnique
<DCTile
>(mDCLayerTree
);
1323 if (!tile
->Initialize(aX
, aY
, mTileSize
, mIsVirtualSurface
, mIsOpaque
,
1325 gfxCriticalNote
<< "Failed to initialize DCTile: " << aX
<< aY
;
1329 if (mIsVirtualSurface
) {
1330 mAllocatedRectDirty
= true;
1332 mVisual
->AddVisual(tile
->GetVisual(), false, nullptr);
1335 mDCTiles
[key
] = std::move(tile
);
1338 void DCSurface::DestroyTile(int32_t aX
, int32_t aY
) {
1339 TileKey
key(aX
, aY
);
1340 if (mIsVirtualSurface
) {
1341 mAllocatedRectDirty
= true;
1343 auto tile
= GetTile(aX
, aY
);
1344 mVisual
->RemoveVisual(tile
->GetVisual());
1346 mDCTiles
.erase(key
);
1349 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty
= true; }
1351 void DCSurface::UpdateAllocatedRect() {
1352 if (mAllocatedRectDirty
) {
1353 if (mVirtualSurface
) {
1354 // The virtual surface may have holes in it (for example, an empty tile
1355 // that has no primitives). Instead of trimming to a single bounding
1356 // rect, supply the rect of each valid tile to handle this case.
1357 std::vector
<RECT
> validRects
;
1359 for (auto it
= mDCTiles
.begin(); it
!= mDCTiles
.end(); ++it
) {
1360 auto tile
= GetTile(it
->first
.mX
, it
->first
.mY
);
1363 rect
.left
= (LONG
)(mVirtualOffset
.x
+ it
->first
.mX
* mTileSize
.width
+
1364 tile
->mValidRect
.x
);
1365 rect
.top
= (LONG
)(mVirtualOffset
.y
+ it
->first
.mY
* mTileSize
.height
+
1366 tile
->mValidRect
.y
);
1367 rect
.right
= rect
.left
+ tile
->mValidRect
.width
;
1368 rect
.bottom
= rect
.top
+ tile
->mValidRect
.height
;
1370 validRects
.push_back(rect
);
1373 mVirtualSurface
->Trim(validRects
.data(), validRects
.size());
1375 // When not using a virtual surface, we still want to reset this
1376 mAllocatedRectDirty
= false;
1380 DCTile
* DCSurface::GetTile(int32_t aX
, int32_t aY
) const {
1381 TileKey
key(aX
, aY
);
1382 auto tile_it
= mDCTiles
.find(key
);
1383 MOZ_RELEASE_ASSERT(tile_it
!= mDCTiles
.end());
1384 return tile_it
->second
.get();
1387 DCSwapChain::~DCSwapChain() {
1389 const auto gl
= mDCLayerTree
->GetGLContext();
1391 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1392 const auto& egl
= gle
->mEgl
;
1393 egl
->fDestroySurface(mEGLSurface
);
1394 mEGLSurface
= EGL_NO_SURFACE
;
1398 bool DCSwapChain::Initialize() {
1399 DCSurface::Initialize();
1401 const auto gl
= mDCLayerTree
->GetGLContext();
1402 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1403 const auto& egl
= gle
->mEgl
;
1406 auto device
= mDCLayerTree
->GetDevice();
1408 RefPtr
<IDXGIDevice
> dxgiDevice
;
1409 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1411 RefPtr
<IDXGIFactory2
> dxgiFactory
;
1413 RefPtr
<IDXGIAdapter
> adapter
;
1414 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1416 IID_PPV_ARGS((IDXGIFactory2
**)getter_AddRefs(dxgiFactory
)));
1419 DXGI_SWAP_CHAIN_DESC1 desc
{};
1420 desc
.Width
= mSize
.width
;
1421 desc
.Height
= mSize
.height
;
1422 desc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
1423 desc
.SampleDesc
.Count
= 1;
1424 desc
.SampleDesc
.Quality
= 0;
1425 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1426 desc
.BufferCount
= 2;
1427 // DXGI_SCALING_NONE caused swap chain creation failure.
1428 desc
.Scaling
= DXGI_SCALING_STRETCH
;
1429 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1431 mIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1434 hr
= dxgiFactory
->CreateSwapChainForComposition(device
, &desc
, nullptr,
1435 getter_AddRefs(mSwapChain
));
1436 MOZ_RELEASE_ASSERT(SUCCEEDED(hr
));
1437 mVisual
->SetContent(mSwapChain
);
1439 ID3D11Texture2D
* backBuffer
;
1440 hr
= mSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
), (void**)&backBuffer
);
1441 MOZ_RELEASE_ASSERT(SUCCEEDED(hr
));
1443 const EGLint pbuffer_attribs
[]{LOCAL_EGL_WIDTH
, mSize
.width
, LOCAL_EGL_HEIGHT
,
1444 mSize
.height
, LOCAL_EGL_NONE
};
1445 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuffer
);
1446 EGLConfig eglConfig
= mDCLayerTree
->GetEGLConfig();
1448 mEGLSurface
= egl
->fCreatePbufferFromClientBuffer(
1449 LOCAL_EGL_D3D_TEXTURE_ANGLE
, buffer
, eglConfig
, pbuffer_attribs
);
1450 MOZ_RELEASE_ASSERT(mEGLSurface
);
1451 backBuffer
->Release();
1456 void DCSwapChain::Bind() {
1457 const auto gl
= mDCLayerTree
->GetGLContext();
1458 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1460 gle
->SetEGLSurfaceOverride(mEGLSurface
);
1461 bool ok
= gl
->MakeCurrent();
1463 MOZ_RELEASE_ASSERT(ok
);
1466 void DCSwapChain::Resize(wr::DeviceIntSize aSize
) {
1467 const auto gl
= mDCLayerTree
->GetGLContext();
1469 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1470 const auto& egl
= gle
->mEgl
;
1473 egl
->fDestroySurface(mEGLSurface
);
1474 mEGLSurface
= EGL_NO_SURFACE
;
1477 ID3D11Texture2D
* backBuffer
;
1478 DXGI_SWAP_CHAIN_DESC desc
;
1481 hr
= mSwapChain
->GetDesc(&desc
);
1482 MOZ_RELEASE_ASSERT(SUCCEEDED(hr
));
1484 hr
= mSwapChain
->ResizeBuffers(desc
.BufferCount
, aSize
.width
, aSize
.height
,
1485 DXGI_FORMAT_B8G8R8A8_UNORM
, 0);
1486 MOZ_RELEASE_ASSERT(SUCCEEDED(hr
));
1488 hr
= mSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
), (void**)&backBuffer
);
1489 MOZ_RELEASE_ASSERT(SUCCEEDED(hr
));
1491 const EGLint pbuffer_attribs
[]{LOCAL_EGL_WIDTH
, aSize
.width
, LOCAL_EGL_HEIGHT
,
1492 aSize
.height
, LOCAL_EGL_NONE
};
1493 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuffer
);
1494 EGLConfig eglConfig
= mDCLayerTree
->GetEGLConfig();
1496 mEGLSurface
= egl
->fCreatePbufferFromClientBuffer(
1497 LOCAL_EGL_D3D_TEXTURE_ANGLE
, buffer
, eglConfig
, pbuffer_attribs
);
1498 MOZ_RELEASE_ASSERT(mEGLSurface
);
1500 backBuffer
->Release();
1505 void DCSwapChain::Present() {
1506 const auto gl
= mDCLayerTree
->GetGLContext();
1507 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1509 HRESULT hr
= mSwapChain
->Present(0, 0);
1510 MOZ_RELEASE_ASSERT(SUCCEEDED(hr
));
1512 gle
->SetEGLSurfaceOverride(EGL_NO_SURFACE
);
1515 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1516 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1518 mSwapChainBufferCount(
1519 StaticPrefs::gfx_webrender_dcomp_video_force_triple_buffering() ? 3
1523 DCSurfaceVideo::~DCSurfaceVideo() {
1524 ReleaseDecodeSwapChainResources();
1525 MOZ_ASSERT(!mSwapChainSurfaceHandle
);
1528 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat
) {
1529 if (aFormat
== DXGI_FORMAT_NV12
|| aFormat
== DXGI_FORMAT_YUY2
) {
1535 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1536 auto [texture
, usageInfo
] =
1537 RenderThread::Get()->GetRenderTextureAndUsageInfo(aExternalImage
);
1538 MOZ_RELEASE_ASSERT(texture
);
1541 mRenderTextureHostUsageInfo
= usageInfo
;
1544 if (mPrevTexture
== texture
) {
1548 // XXX if software decoded video frame format is nv12, it could be used as
1550 if (!texture
|| !texture
->AsRenderDXGITextureHost() ||
1551 texture
->GetFormat() != gfx::SurfaceFormat::NV12
) {
1552 gfxCriticalNote
<< "Unsupported RenderTexture for overlay: "
1553 << gfx::hexa(texture
);
1557 mRenderTextureHost
= texture
;
1560 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix
& aTransform
) {
1561 if (!mRenderTextureHost
) {
1562 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1566 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1567 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1568 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1569 mDCLayerTree
->SetUsedOverlayTypeInFrame(overlayType
);
1571 mVideoSize
= mRenderTextureHost
->AsRenderDXGITextureHost()->GetSize(0);
1573 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1574 // DCSurfaceVideo::PresentVideo() needs to be called.
1575 bool needsToPresent
= mPrevTexture
!= mRenderTextureHost
;
1576 gfx::IntSize swapChainSize
= mVideoSize
;
1577 gfx::Matrix transform
= aTransform
;
1578 const bool isDRM
= mRenderTextureHost
->IsFromDRMSource();
1580 // When video is rendered to axis aligned integer rectangle, video scaling
1581 // could be done by VideoProcessor
1582 bool scaleVideoAtVideoProcessor
= false;
1583 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1584 aTransform
.PreservesAxisAlignedRectangles()) {
1585 gfx::Size scaledSize
= gfx::Size(mVideoSize
) * aTransform
.ScaleFactors();
1586 gfx::IntSize
size(int32_t(std::round(scaledSize
.width
)),
1587 int32_t(std::round(scaledSize
.height
)));
1588 if (gfx::FuzzyEqual(scaledSize
.width
, size
.width
, 0.1f
) &&
1589 gfx::FuzzyEqual(scaledSize
.height
, size
.height
, 0.1f
)) {
1590 scaleVideoAtVideoProcessor
= true;
1591 swapChainSize
= size
;
1595 if (scaleVideoAtVideoProcessor
) {
1596 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1597 // subsampled formats like NV12 must have an even width and height.
1598 if (swapChainSize
.width
% 2 == 1) {
1599 swapChainSize
.width
+= 1;
1601 if (swapChainSize
.height
% 2 == 1) {
1602 swapChainSize
.height
+= 1;
1604 transform
= gfx::Matrix::Translation(aTransform
.GetTranslation());
1607 if (!mDCLayerTree
->EnsureVideoProcessor(mVideoSize
, swapChainSize
)) {
1608 gfxCriticalNote
<< "EnsureVideoProcessor Failed";
1612 MOZ_ASSERT(mDCLayerTree
->GetVideoContext());
1613 MOZ_ASSERT(mDCLayerTree
->GetVideoProcessor());
1615 const UINT vendorId
= GetVendorId(mDCLayerTree
->GetVideoDevice());
1616 const bool driverSupportsAutoHDR
=
1617 GetVpAutoHDRSupported(vendorId
, mDCLayerTree
->GetVideoContext(),
1618 mDCLayerTree
->GetVideoProcessor());
1619 const bool contentIsHDR
= false; // XXX for now, only non-HDR is supported.
1620 const bool monitorIsHDR
=
1621 gfx::DeviceManagerDx::Get()->WindowHDREnabled(mDCLayerTree
->GetHwnd());
1622 const bool powerIsCharging
= RenderThread::Get()->GetPowerIsCharging();
1624 bool useVpAutoHDR
= gfx::gfxVars::WebRenderOverlayVpAutoHDR() &&
1625 !contentIsHDR
&& monitorIsHDR
&& driverSupportsAutoHDR
&&
1626 powerIsCharging
&& !mVpAutoHDRFailed
;
1628 if (profiler_thread_is_being_profiled_for_markers()) {
1629 nsPrintfCString
str(
1630 "useVpAutoHDR %d gfxVars %d contentIsHDR %d monitor %d driver %d "
1631 "charging %d failed %d",
1632 useVpAutoHDR
, gfx::gfxVars::WebRenderOverlayVpAutoHDR(), contentIsHDR
,
1633 monitorIsHDR
, driverSupportsAutoHDR
, powerIsCharging
, mVpAutoHDRFailed
);
1634 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS
, {}, str
);
1637 if (!mVideoSwapChain
|| mSwapChainSize
!= swapChainSize
|| mIsDRM
!= isDRM
||
1638 mUseVpAutoHDR
!= useVpAutoHDR
) {
1639 needsToPresent
= true;
1640 ReleaseDecodeSwapChainResources();
1641 // Update mSwapChainSize before creating SwapChain
1642 mSwapChainSize
= swapChainSize
;
1645 auto swapChainFormat
= GetSwapChainFormat(useVpAutoHDR
);
1646 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1647 if (useYUVSwapChain
) {
1648 // Tries to create YUV SwapChain
1649 CreateVideoSwapChain(swapChainFormat
);
1650 if (!mVideoSwapChain
) {
1651 mFailedYuvSwapChain
= true;
1652 ReleaseDecodeSwapChainResources();
1654 gfxCriticalNote
<< "Fallback to RGB SwapChain";
1657 // Tries to create RGB SwapChain
1658 if (!mVideoSwapChain
) {
1659 CreateVideoSwapChain(swapChainFormat
);
1661 if (!mVideoSwapChain
&& useVpAutoHDR
) {
1662 mVpAutoHDRFailed
= true;
1663 gfxCriticalNoteOnce
<< "Failed to create video SwapChain for VpAutoHDR";
1665 // Disable VpAutoHDR
1666 useVpAutoHDR
= false;
1667 swapChainFormat
= GetSwapChainFormat(useVpAutoHDR
);
1668 CreateVideoSwapChain(swapChainFormat
);
1672 aTransform
= transform
;
1673 mUseVpAutoHDR
= useVpAutoHDR
;
1675 return needsToPresent
;
1678 void DCSurfaceVideo::PresentVideo() {
1679 if (!mRenderTextureHost
) {
1683 if (!mVideoSwapChain
) {
1684 gfxCriticalNote
<< "Failed to create VideoSwapChain";
1685 RenderThread::Get()->NotifyWebRenderError(
1686 wr::WebRenderError::VIDEO_OVERLAY
);
1690 mVisual
->SetContent(mVideoSwapChain
);
1692 if (!CallVideoProcessorBlt()) {
1693 bool useYUVSwapChain
= IsYUVSwapChainFormat(mSwapChainFormat
);
1694 if (useYUVSwapChain
) {
1695 mFailedYuvSwapChain
= true;
1696 ReleaseDecodeSwapChainResources();
1699 RenderThread::Get()->NotifyWebRenderError(
1700 wr::WebRenderError::VIDEO_OVERLAY
);
1705 UINT flags
= DXGI_PRESENT_USE_DURATION
;
1708 auto start
= TimeStamp::Now();
1709 hr
= mVideoSwapChain
->Present(interval
, flags
);
1710 auto end
= TimeStamp::Now();
1712 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1713 gfxCriticalNoteOnce
<< "video Present failed: " << gfx::hexa(hr
);
1716 mPrevTexture
= mRenderTextureHost
;
1718 // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops
1721 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
1725 const auto maxPresentWaitDurationMs
= 2;
1726 const auto maxSlowPresentCount
= 5;
1727 const auto presentDurationMs
=
1728 static_cast<uint32_t>((end
- start
).ToMilliseconds());
1729 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1730 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1731 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1733 nsPrintfCString
marker("PresentWait overlay %u %ums ", (uint8_t)overlayType
,
1735 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS
, {}, marker
);
1737 if (presentDurationMs
> maxPresentWaitDurationMs
) {
1738 mSlowPresentCount
++;
1740 mSlowPresentCount
= 0;
1743 if (mSlowPresentCount
<= maxSlowPresentCount
) {
1747 DisableVideoOverlay();
1749 if (overlayType
== DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
1750 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
1752 nsPrintfCString
marker("Sw video swapchain present is slow");
1753 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS
, {}, marker
);
1755 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
1757 nsPrintfCString
marker("Hw video swapchain present is slow");
1758 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS
, {}, marker
);
1762 void DCSurfaceVideo::DisableVideoOverlay() {
1763 if (!mRenderTextureHostUsageInfo
) {
1766 mRenderTextureHostUsageInfo
->DisableVideoOverlay();
1769 DXGI_FORMAT
DCSurfaceVideo::GetSwapChainFormat(bool aUseVpAutoHDR
) {
1770 if (aUseVpAutoHDR
) {
1771 return DXGI_FORMAT_R16G16B16A16_FLOAT
;
1773 if (mFailedYuvSwapChain
|| !mDCLayerTree
->SupportsHardwareOverlays()) {
1774 return DXGI_FORMAT_B8G8R8A8_UNORM
;
1776 return mDCLayerTree
->GetOverlayFormatForSDR();
1779 bool DCSurfaceVideo::CreateVideoSwapChain(DXGI_FORMAT aSwapChainFormat
) {
1780 MOZ_ASSERT(mRenderTextureHost
);
1782 const auto device
= mDCLayerTree
->GetDevice();
1784 RefPtr
<IDXGIDevice
> dxgiDevice
;
1785 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1787 RefPtr
<IDXGIFactoryMedia
> dxgiFactoryMedia
;
1789 RefPtr
<IDXGIAdapter
> adapter
;
1790 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1792 IID_PPV_ARGS((IDXGIFactoryMedia
**)getter_AddRefs(dxgiFactoryMedia
)));
1795 mSwapChainSurfaceHandle
= gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1796 if (!mSwapChainSurfaceHandle
) {
1797 gfxCriticalNote
<< "Failed to create DCompSurfaceHandle";
1801 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1802 desc
.Width
= mSwapChainSize
.width
;
1803 desc
.Height
= mSwapChainSize
.height
;
1804 desc
.Format
= aSwapChainFormat
;
1805 desc
.Stereo
= FALSE
;
1806 desc
.SampleDesc
.Count
= 1;
1807 desc
.BufferCount
= mSwapChainBufferCount
;
1808 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1809 desc
.Scaling
= DXGI_SCALING_STRETCH
;
1810 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1811 desc
.Flags
= DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO
;
1812 if (IsYUVSwapChainFormat(aSwapChainFormat
)) {
1813 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO
;
1816 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY
;
1818 desc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
1821 hr
= dxgiFactoryMedia
->CreateSwapChainForCompositionSurfaceHandle(
1822 device
, mSwapChainSurfaceHandle
, &desc
, nullptr,
1823 getter_AddRefs(mVideoSwapChain
));
1826 gfxCriticalNote
<< "Failed to create video SwapChain: " << gfx::hexa(hr
)
1827 << " " << mSwapChainSize
;
1831 mSwapChainFormat
= aSwapChainFormat
;
1835 // TODO: Replace with YUVRangedColorSpace
1836 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1837 const gfx::YUVColorSpace aYUVColorSpace
,
1838 const gfx::ColorRange aColorRange
) {
1839 if (aYUVColorSpace
== gfx::YUVColorSpace::BT601
) {
1840 if (aColorRange
== gfx::ColorRange::FULL
) {
1841 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601
);
1843 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
);
1845 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT709
) {
1846 if (aColorRange
== gfx::ColorRange::FULL
) {
1847 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
);
1849 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
);
1851 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT2020
) {
1852 if (aColorRange
== gfx::ColorRange::FULL
) {
1853 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1855 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020
);
1857 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
);
1864 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1865 const gfx::YUVRangedColorSpace aYUVColorSpace
) {
1866 const auto info
= FromYUVRangedColorSpace(aYUVColorSpace
);
1867 return GetSourceDXGIColorSpace(info
.space
, info
.range
);
1870 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1871 MOZ_ASSERT(mRenderTextureHost
);
1874 const auto videoDevice
= mDCLayerTree
->GetVideoDevice();
1875 const auto videoContext
= mDCLayerTree
->GetVideoContext();
1876 const auto texture
= mRenderTextureHost
->AsRenderDXGITextureHost();
1878 Maybe
<DXGI_COLOR_SPACE_TYPE
> sourceColorSpace
=
1879 GetSourceDXGIColorSpace(texture
->GetYUVColorSpace());
1880 if (sourceColorSpace
.isNothing()) {
1881 gfxCriticalNote
<< "Unsupported color space";
1885 RefPtr
<ID3D11Texture2D
> texture2D
= texture
->GetD3D11Texture2DWithGL();
1887 gfxCriticalNote
<< "Failed to get D3D11Texture2D";
1891 if (!mVideoSwapChain
) {
1895 auto query
= texture
->GetQuery();
1897 // Wait ID3D11Query of D3D11Texture2D copy complete just before blitting for
1898 // video overlay with non Intel GPUs. See Bug 1817617.
1900 bool ret
= layers::WaitForFrameGPUQuery(mDCLayerTree
->GetDevice(),
1901 mDCLayerTree
->GetDeviceContext(),
1904 gfxCriticalNoteOnce
<< "WaitForFrameGPUQuery() failed";
1908 RefPtr
<IDXGISwapChain3
> swapChain3
;
1909 mVideoSwapChain
->QueryInterface(
1910 (IDXGISwapChain3
**)getter_AddRefs(swapChain3
));
1912 gfxCriticalNote
<< "Failed to get IDXGISwapChain3";
1916 RefPtr
<ID3D11VideoContext1
> videoContext1
;
1917 videoContext
->QueryInterface(
1918 (ID3D11VideoContext1
**)getter_AddRefs(videoContext1
));
1919 if (!videoContext1
) {
1920 gfxCriticalNote
<< "Failed to get ID3D11VideoContext1";
1924 const auto videoProcessor
= mDCLayerTree
->GetVideoProcessor();
1925 const auto videoProcessorEnumerator
=
1926 mDCLayerTree
->GetVideoProcessorEnumerator();
1928 DXGI_COLOR_SPACE_TYPE inputColorSpace
= sourceColorSpace
.ref();
1929 videoContext1
->VideoProcessorSetStreamColorSpace1(videoProcessor
, 0,
1932 DXGI_COLOR_SPACE_TYPE outputColorSpace
=
1933 IsYUVSwapChainFormat(mSwapChainFormat
)
1935 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1937 if (mUseVpAutoHDR
) {
1938 outputColorSpace
= mSwapChainFormat
== DXGI_FORMAT_R16G16B16A16_FLOAT
1939 ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709
1940 : DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
;
1943 hr
= swapChain3
->SetColorSpace1(outputColorSpace
);
1945 gfxCriticalNoteOnce
<< "SetColorSpace1 failed: " << gfx::hexa(hr
);
1946 RenderThread::Get()->NotifyWebRenderError(
1947 wr::WebRenderError::VIDEO_OVERLAY
);
1950 videoContext1
->VideoProcessorSetOutputColorSpace1(videoProcessor
,
1953 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc
= {};
1954 inputDesc
.ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
;
1955 inputDesc
.Texture2D
.ArraySlice
= texture
->ArrayIndex();
1957 RefPtr
<ID3D11VideoProcessorInputView
> inputView
;
1958 hr
= videoDevice
->CreateVideoProcessorInputView(
1959 texture2D
, videoProcessorEnumerator
, &inputDesc
,
1960 getter_AddRefs(inputView
));
1962 gfxCriticalNote
<< "ID3D11VideoProcessorInputView creation failed: "
1967 D3D11_VIDEO_PROCESSOR_STREAM stream
= {};
1968 stream
.Enable
= true;
1969 stream
.OutputIndex
= 0;
1970 stream
.InputFrameOrField
= 0;
1971 stream
.PastFrames
= 0;
1972 stream
.FutureFrames
= 0;
1973 stream
.pInputSurface
= inputView
.get();
1978 destRect
.right
= mSwapChainSize
.width
;
1979 destRect
.bottom
= mSwapChainSize
.height
;
1981 videoContext
->VideoProcessorSetOutputTargetRect(videoProcessor
, TRUE
,
1983 videoContext
->VideoProcessorSetStreamDestRect(videoProcessor
, 0, TRUE
,
1986 sourceRect
.left
= 0;
1988 sourceRect
.right
= mVideoSize
.width
;
1989 sourceRect
.bottom
= mVideoSize
.height
;
1990 videoContext
->VideoProcessorSetStreamSourceRect(videoProcessor
, 0, TRUE
,
1994 RefPtr
<ID3D11Texture2D
> backBuf
;
1995 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1996 (void**)getter_AddRefs(backBuf
));
1998 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc
= {};
1999 outputDesc
.ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2D
;
2000 outputDesc
.Texture2D
.MipSlice
= 0;
2002 hr
= videoDevice
->CreateVideoProcessorOutputView(
2003 backBuf
, videoProcessorEnumerator
, &outputDesc
,
2004 getter_AddRefs(mOutputView
));
2006 gfxCriticalNote
<< "ID3D11VideoProcessorOutputView creation failed: "
2012 const UINT vendorId
= GetVendorId(videoDevice
);
2013 const auto powerIsCharging
= RenderThread::Get()->GetPowerIsCharging();
2014 const bool useSuperResolution
=
2015 gfx::gfxVars::WebRenderOverlayVpSuperResolution() && powerIsCharging
&&
2016 !mVpSuperResolutionFailed
;
2018 if (profiler_thread_is_being_profiled_for_markers()) {
2019 nsPrintfCString
str(
2020 "useSuperResolution %d gfxVars %d charging %d failed %d",
2021 useSuperResolution
, gfx::gfxVars::WebRenderOverlayVpSuperResolution(),
2022 powerIsCharging
, mVpSuperResolutionFailed
);
2023 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS
, {}, str
);
2026 if (useSuperResolution
) {
2027 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS
, {},
2028 "SetVpSuperResolution"_ns
);
2030 hr
= SetVpSuperResolution(vendorId
, videoContext
, videoProcessor
, true);
2032 if (hr
!= E_NOTIMPL
) {
2033 gfxCriticalNoteOnce
<< "SetVpSuperResolution failed: " << gfx::hexa(hr
);
2035 mVpSuperResolutionFailed
= true;
2037 } else if (gfx::gfxVars::WebRenderOverlayVpSuperResolution() &&
2038 !useSuperResolution
) {
2039 SetVpSuperResolution(vendorId
, videoContext
, videoProcessor
, false);
2042 if (profiler_thread_is_being_profiled_for_markers() && vendorId
== 0x10DE) {
2043 AddProfileMarkerForNvidiaVpSuperResolutionInfo(videoContext
,
2047 if (mUseVpAutoHDR
) {
2048 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS
, {}, "SetVpAutoHDR"_ns
);
2050 hr
= SetVpAutoHDR(vendorId
, videoContext
, videoProcessor
, true);
2052 gfxCriticalNoteOnce
<< "SetVpAutoHDR failed: " << gfx::hexa(hr
);
2053 mVpAutoHDRFailed
= true;
2057 hr
= videoContext
->VideoProcessorBlt(videoProcessor
, mOutputView
, 0, 1,
2060 gfxCriticalNote
<< "VideoProcessorBlt failed: " << gfx::hexa(hr
);
2067 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
2068 mOutputView
= nullptr;
2069 mVideoSwapChain
= nullptr;
2070 mDecodeSwapChain
= nullptr;
2071 mDecodeResource
= nullptr;
2072 if (mSwapChainSurfaceHandle
) {
2073 ::CloseHandle(mSwapChainSurfaceHandle
);
2074 mSwapChainSurfaceHandle
= 0;
2076 mUseVpAutoHDR
= false;
2079 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
2080 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
2083 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
2084 RenderTextureHost
* texture
=
2085 RenderThread::Get()->GetRenderTexture(aExternalImage
);
2086 RenderDcompSurfaceTextureHost
* renderTexture
=
2087 texture
? texture
->AsRenderDcompSurfaceTextureHost() : nullptr;
2088 if (!renderTexture
) {
2089 gfxCriticalNote
<< "Unsupported RenderTexture for DCSurfaceHandle: "
2090 << gfx::hexa(texture
);
2094 const auto handle
= renderTexture
->GetDcompSurfaceHandle();
2095 if (GetSurfaceHandle() == handle
) {
2099 LOG_H("AttachExternalImage, ext-image=%" PRIu64
", texture=%p, handle=%p",
2100 wr::AsUint64(aExternalImage
), renderTexture
, handle
);
2101 mDcompTextureHost
= renderTexture
;
2104 HANDLE
DCSurfaceHandle::GetSurfaceHandle() const {
2105 if (mDcompTextureHost
) {
2106 return mDcompTextureHost
->GetDcompSurfaceHandle();
2111 IDCompositionSurface
* DCSurfaceHandle::EnsureSurface() {
2112 if (auto* surface
= mDcompTextureHost
->GetSurface()) {
2116 // Texture host hasn't created the surface yet, ask it to create a new one.
2117 RefPtr
<IDCompositionDevice
> device
;
2118 HRESULT hr
= mDCLayerTree
->GetCompositionDevice()->QueryInterface(
2119 (IDCompositionDevice
**)getter_AddRefs(device
));
2122 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
2127 return mDcompTextureHost
->CreateSurfaceFromDevice(device
);
2130 void DCSurfaceHandle::PresentSurfaceHandle() {
2131 LOG_H("PresentSurfaceHandle");
2132 if (IDCompositionSurface
* surface
= EnsureSurface()) {
2133 LOG_H("Set surface %p to visual", surface
);
2134 mVisual
->SetContent(surface
);
2136 mVisual
->SetContent(nullptr);
2140 DCTile::DCTile(DCLayerTree
* aDCLayerTree
) : mDCLayerTree(aDCLayerTree
) {}
2142 DCTile::~DCTile() {}
2144 bool DCTile::Initialize(int aX
, int aY
, wr::DeviceIntSize aSize
,
2145 bool aIsVirtualSurface
, bool aIsOpaque
,
2146 RefPtr
<IDCompositionVisual2
> mSurfaceVisual
) {
2147 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
2152 mIsOpaque
= aIsOpaque
;
2153 mIsVirtualSurface
= aIsVirtualSurface
;
2154 mNeedsFullDraw
= !aIsVirtualSurface
;
2156 if (aIsVirtualSurface
) {
2157 // Initially, the entire tile is considered valid, unless it is set by
2158 // the SetTileProperties method.
2161 mValidRect
.width
= aSize
.width
;
2162 mValidRect
.height
= aSize
.height
;
2165 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
2166 // Create the visual and put it in the tree under the surface visual
2167 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
2169 gfxCriticalNote
<< "Failed to CreateVisual for DCTile: " << gfx::hexa(hr
);
2172 mSurfaceVisual
->AddVisual(mVisual
, false, nullptr);
2173 // Position the tile relative to the surface visual
2174 mVisual
->SetOffsetX(aX
* aSize
.width
);
2175 mVisual
->SetOffsetY(aY
* aSize
.height
);
2176 // Clip the visual so it doesn't show anything until we update it
2177 D2D_RECT_F clip
= {0, 0, 0, 0};
2178 mVisual
->SetClip(clip
);
2179 // Create the underlying pixel buffer.
2180 mCompositionSurface
= CreateCompositionSurface(aSize
, aIsOpaque
);
2181 if (!mCompositionSurface
) {
2184 hr
= mVisual
->SetContent(mCompositionSurface
);
2186 gfxCriticalNote
<< "Failed to SetContent for DCTile: " << gfx::hexa(hr
);
2194 RefPtr
<IDCompositionSurface
> DCTile::CreateCompositionSurface(
2195 wr::DeviceIntSize aSize
, bool aIsOpaque
) {
2197 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
2198 const auto alphaMode
=
2199 aIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
2200 RefPtr
<IDCompositionSurface
> compositionSurface
;
2202 hr
= dCompDevice
->CreateSurface(aSize
.width
, aSize
.height
,
2203 DXGI_FORMAT_R8G8B8A8_UNORM
, alphaMode
,
2204 getter_AddRefs(compositionSurface
));
2206 gfxCriticalNote
<< "Failed to CreateSurface for DCTile: " << gfx::hexa(hr
);
2209 return compositionSurface
;
2212 RefPtr
<IDCompositionSurface
> DCTile::Bind(wr::DeviceIntRect aValidRect
) {
2213 if (mVisual
!= nullptr) {
2214 // Tile owns a visual, set the size of the visual to match the portion we
2215 // want to be visible.
2216 D2D_RECT_F clip_rect
;
2217 clip_rect
.left
= aValidRect
.min
.x
;
2218 clip_rect
.top
= aValidRect
.min
.y
;
2219 clip_rect
.right
= aValidRect
.max
.x
;
2220 clip_rect
.bottom
= aValidRect
.max
.y
;
2221 mVisual
->SetClip(clip_rect
);
2223 return mCompositionSurface
;
2226 GLuint
DCLayerTree::CreateEGLSurfaceForCompositionSurface(
2227 wr::DeviceIntRect aDirtyRect
, wr::DeviceIntPoint
* aOffset
,
2228 RefPtr
<IDCompositionSurface
> aCompositionSurface
,
2229 wr::DeviceIntPoint aSurfaceOffset
) {
2230 MOZ_ASSERT(aCompositionSurface
.get());
2233 const auto gl
= GetGLContext();
2234 RefPtr
<ID3D11Texture2D
> backBuf
;
2238 update_rect
.left
= aSurfaceOffset
.x
+ aDirtyRect
.min
.x
;
2239 update_rect
.top
= aSurfaceOffset
.y
+ aDirtyRect
.min
.y
;
2240 update_rect
.right
= aSurfaceOffset
.x
+ aDirtyRect
.max
.x
;
2241 update_rect
.bottom
= aSurfaceOffset
.y
+ aDirtyRect
.max
.y
;
2242 hr
= aCompositionSurface
->BeginDraw(&update_rect
, __uuidof(ID3D11Texture2D
),
2243 (void**)getter_AddRefs(backBuf
), &offset
);
2246 LayoutDeviceIntRect rect
= widget::WinUtils::ToIntRect(update_rect
);
2248 gfxCriticalNote
<< "DCompositionSurface::BeginDraw failed: "
2249 << gfx::hexa(hr
) << " " << rect
;
2250 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW
);
2254 // DC includes the origin of the dirty / update rect in the draw offset,
2255 // undo that here since WR expects it to be an absolute offset.
2256 offset
.x
-= aDirtyRect
.min
.x
;
2257 offset
.y
-= aDirtyRect
.min
.y
;
2259 D3D11_TEXTURE2D_DESC desc
;
2260 backBuf
->GetDesc(&desc
);
2262 const auto& gle
= gl::GLContextEGL::Cast(gl
);
2263 const auto& egl
= gle
->mEgl
;
2265 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
2267 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
2268 const EGLint attribs
[] = {LOCAL_EGL_NONE
};
2269 mEGLImage
= egl
->fCreateImage(EGL_NO_CONTEXT
, LOCAL_EGL_D3D11_TEXTURE_ANGLE
,
2272 // Get the current FBO and RBO id, so we can restore them later
2273 GLint currentFboId
, currentRboId
;
2274 gl
->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
, ¤tFboId
);
2275 gl
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, ¤tRboId
);
2277 // Create a render buffer object that is backed by the EGL image.
2278 gl
->fGenRenderbuffers(1, &mColorRBO
);
2279 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, mColorRBO
);
2280 gl
->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, mEGLImage
);
2282 // Get or create an FBO for the specified dimensions
2283 GLuint fboId
= GetOrCreateFbo(desc
.Width
, desc
.Height
);
2285 // Attach the new renderbuffer to the FBO
2286 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
2287 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
2288 LOCAL_GL_COLOR_ATTACHMENT0
,
2289 LOCAL_GL_RENDERBUFFER
, mColorRBO
);
2291 // Restore previous FBO and RBO bindings
2292 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, currentFboId
);
2293 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, currentRboId
);
2295 aOffset
->x
= offset
.x
;
2296 aOffset
->y
= offset
.y
;
2301 void DCLayerTree::DestroyEGLSurface() {
2302 const auto gl
= GetGLContext();
2305 gl
->fDeleteRenderbuffers(1, &mColorRBO
);
2310 const auto& gle
= gl::GLContextEGL::Cast(gl
);
2311 const auto& egl
= gle
->mEgl
;
2312 egl
->fDestroyImage(mEGLImage
);
2313 mEGLImage
= EGL_NO_IMAGE
;
2322 color::ColorProfileDesc
QueryOutputColorProfile() {
2323 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
2324 // but we do need gfxPlatform::GetCMSOutputProfile().
2325 // So we steal what we need through the window:
2326 const auto outputProfileData
=
2327 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
2329 const auto qcmsProfile
= qcms_profile_from_memory(
2330 outputProfileData
.Elements(), outputProfileData
.Length());
2331 const auto release
= MakeScopeExit([&]() {
2333 qcms_profile_release(qcmsProfile
);
2337 const bool print
= gfxEnv::MOZ_GL_SPEW();
2339 const auto ret
= [&]() {
2341 return color::ColorProfileDesc::From(*qcmsProfile
);
2345 "Missing or failed to load display color profile, defaulting to "
2348 const auto MISSING_PROFILE_DEFAULT_SPACE
= color::ColorspaceDesc
{
2349 color::Chromaticities::Srgb(),
2350 color::PiecewiseGammaDesc::Srgb(),
2352 return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE
);
2356 const auto gammaGuess
= color::GuessGamma(ret
.linearFromTf
.r
);
2358 "Display profile:\n"
2359 " Approx Gamma: %f\n"
2360 " XYZ-D65 Red : %f, %f, %f\n"
2361 " XYZ-D65 Green: %f, %f, %f\n"
2362 " XYZ-D65 Blue : %f, %f, %f\n",
2363 gammaGuess
, ret
.xyzd65FromLinearRgb
.at(0, 0),
2364 ret
.xyzd65FromLinearRgb
.at(0, 1), ret
.xyzd65FromLinearRgb
.at(0, 2),
2366 ret
.xyzd65FromLinearRgb
.at(1, 0), ret
.xyzd65FromLinearRgb
.at(1, 1),
2367 ret
.xyzd65FromLinearRgb
.at(1, 2),
2369 ret
.xyzd65FromLinearRgb
.at(2, 0), ret
.xyzd65FromLinearRgb
.at(2, 1),
2370 ret
.xyzd65FromLinearRgb
.at(2, 2));
2379 inline D2D1_MATRIX_5X4_F
to_D2D1_MATRIX_5X4_F(const color::mat4
& m
) {
2380 return D2D1_MATRIX_5X4_F
{{{
2404 ColorManagementChain
ColorManagementChain::From(
2405 IDCompositionDevice3
& dcomp
,
2406 const color::ColorProfileConversionDesc
& conv
) {
2407 auto ret
= ColorManagementChain
{};
2409 const auto Append
= [&](const RefPtr
<IDCompositionFilterEffect
>& afterLast
) {
2411 afterLast
->SetInput(0, ret
.last
, 0);
2413 ret
.last
= afterLast
;
2416 const auto MaybeAppendColorMatrix
= [&](const color::mat4
& m
) {
2417 RefPtr
<IDCompositionColorMatrixEffect
> e
;
2418 if (approx(m
, color::mat4::Identity())) return e
;
2419 dcomp
.CreateColorMatrixEffect(getter_AddRefs(e
));
2422 e
->SetMatrix(to_D2D1_MATRIX_5X4_F(m
));
2426 const auto MaybeAppendTableTransfer
= [&](const color::RgbTransferTables
& t
) {
2427 RefPtr
<IDCompositionTableTransferEffect
> e
;
2428 if (!t
.r
.size() && !t
.g
.size() && !t
.b
.size()) return e
;
2429 dcomp
.CreateTableTransferEffect(getter_AddRefs(e
));
2432 e
->SetRedTable(t
.r
.data(), t
.r
.size());
2433 e
->SetGreenTable(t
.g
.data(), t
.g
.size());
2434 e
->SetBlueTable(t
.b
.data(), t
.b
.size());
2439 ret
.srcRgbFromSrcYuv
= MaybeAppendColorMatrix(conv
.srcRgbFromSrcYuv
);
2440 ret
.srcLinearFromSrcTf
= MaybeAppendTableTransfer(conv
.srcLinearFromSrcTf
);
2441 ret
.dstLinearFromSrcLinear
=
2442 MaybeAppendColorMatrix(color::mat4(conv
.dstLinearFromSrcLinear
));
2443 ret
.dstTfFromDstLinear
= MaybeAppendTableTransfer(conv
.dstTfFromDstLinear
);
2448 ColorManagementChain::~ColorManagementChain() = default;
2451 } // namespace mozilla