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"
40 #if defined(__MINGW32__) // 64 defines both 32 and 64
41 // We need to fake some things, while we wait on updates to mingw's dcomp.h
42 // header. Just enough that we can successfully fail to work there.
43 # define MOZ_MINGW_DCOMP_H_INCOMPLETE
44 struct IDCompositionColorMatrixEffect
: public IDCompositionFilterEffect
{};
45 struct IDCompositionTableTransferEffect
: public IDCompositionFilterEffect
{};
46 #endif // defined(__MINGW32__)
51 extern LazyLogModule gRenderThreadLog
;
52 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
54 #define LOG_H(msg, ...) \
55 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
56 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
58 StaticAutoPtr
<GpuOverlayInfo
> DCLayerTree::sGpuOverlayInfo
;
61 UniquePtr
<DCLayerTree
> DCLayerTree::Create(gl::GLContext
* aGL
,
63 ID3D11Device
* aDevice
,
64 ID3D11DeviceContext
* aCtx
,
65 HWND aHwnd
, nsACString
& aError
) {
66 RefPtr
<IDCompositionDevice2
> dCompDevice
=
67 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
69 aError
.Assign("DCLayerTree(no device)"_ns
);
74 MakeUnique
<DCLayerTree
>(aGL
, aEGLConfig
, aDevice
, aCtx
, dCompDevice
);
75 if (!layerTree
->Initialize(aHwnd
, aError
)) {
82 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo
= nullptr; }
84 DCLayerTree::DCLayerTree(gl::GLContext
* aGL
, EGLConfig aEGLConfig
,
85 ID3D11Device
* aDevice
, ID3D11DeviceContext
* aCtx
,
86 IDCompositionDevice2
* aCompositionDevice
)
88 mEGLConfig(aEGLConfig
),
91 mCompositionDevice(aCompositionDevice
),
93 mDebugVisualRedrawRegions(false),
94 mEGLImage(EGL_NO_IMAGE
),
96 mPendingCommit(false) {
97 LOG("DCLayerTree::DCLayerTree()");
100 DCLayerTree::~DCLayerTree() {
101 LOG("DCLayerTree::~DCLayerTree()");
103 ReleaseNativeCompositorResources();
106 void DCLayerTree::ReleaseNativeCompositorResources() {
107 const auto gl
= GetGLContext();
111 // Delete any cached FBO objects
112 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
113 gl
->fDeleteRenderbuffers(1, &it
->depthRboId
);
114 gl
->fDeleteFramebuffers(1, &it
->fboId
);
118 bool DCLayerTree::Initialize(HWND aHwnd
, nsACString
& aError
) {
121 RefPtr
<IDCompositionDesktopDevice
> desktopDevice
;
122 hr
= mCompositionDevice
->QueryInterface(
123 (IDCompositionDesktopDevice
**)getter_AddRefs(desktopDevice
));
125 aError
.Assign(nsPrintfCString(
126 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr
));
130 hr
= desktopDevice
->CreateTargetForHwnd(aHwnd
, TRUE
,
131 getter_AddRefs(mCompositionTarget
));
133 aError
.Assign(nsPrintfCString(
134 "DCLayerTree(create DCompositionTarget failed %lx)", hr
));
138 hr
= mCompositionDevice
->CreateVisual(getter_AddRefs(mRootVisual
));
140 aError
.Assign(nsPrintfCString(
141 "DCLayerTree(create root DCompositionVisual failed %lx)", hr
));
146 mCompositionDevice
->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual
));
148 aError
.Assign(nsPrintfCString(
149 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr
));
153 if (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin() ||
154 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) {
155 if (!InitializeVideoOverlaySupport()) {
156 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY
);
159 if (!sGpuOverlayInfo
) {
160 // Set default if sGpuOverlayInfo was not set.
161 sGpuOverlayInfo
= new GpuOverlayInfo();
164 // Initialize SwapChainInfo
165 SupportsSwapChainTearing();
167 mCompositionTarget
->SetRoot(mRootVisual
);
168 // Set interporation mode to nearest, to ensure 1:1 sampling.
169 // By default, a visual inherits the interpolation mode of the parent visual.
170 // If no visuals set the interpolation mode, the default for the entire visual
171 // tree is nearest neighbor interpolation.
172 mRootVisual
->SetBitmapInterpolationMode(
173 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
177 bool FlagsSupportsOverlays(UINT flags
) {
178 return (flags
& (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
|
179 DXGI_OVERLAY_SUPPORT_FLAG_SCALING
));
182 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
183 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat
,
184 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace
,
185 RefPtr
<IDXGIOutput
> aOutput
,
186 RefPtr
<ID3D11Device
> aD3d11Device
) {
187 UINT colorSpaceSupportFlags
= 0;
188 RefPtr
<IDXGIOutput4
> output4
;
190 if (FAILED(aOutput
->QueryInterface(__uuidof(IDXGIOutput4
),
191 getter_AddRefs(output4
)))) {
195 if (FAILED(output4
->CheckOverlayColorSpaceSupport(
196 aDxgiFormat
, aDxgiColorSpace
, aD3d11Device
,
197 &colorSpaceSupportFlags
))) {
201 return (colorSpaceSupportFlags
&
202 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT
);
205 bool DCLayerTree::InitializeVideoOverlaySupport() {
206 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
210 hr
= mDevice
->QueryInterface(
211 (ID3D11VideoDevice
**)getter_AddRefs(mVideoDevice
));
213 gfxCriticalNote
<< "Failed to get D3D11VideoDevice: " << gfx::hexa(hr
);
218 mCtx
->QueryInterface((ID3D11VideoContext
**)getter_AddRefs(mVideoContext
));
220 gfxCriticalNote
<< "Failed to get D3D11VideoContext: " << gfx::hexa(hr
);
224 if (sGpuOverlayInfo
) {
228 UniquePtr
<GpuOverlayInfo
> info
= MakeUnique
<GpuOverlayInfo
>();
230 RefPtr
<IDXGIDevice
> dxgiDevice
;
231 RefPtr
<IDXGIAdapter
> adapter
;
232 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
233 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
237 RefPtr
<IDXGIOutput
> output
;
238 if (FAILED(adapter
->EnumOutputs(i
++, getter_AddRefs(output
)))) {
241 RefPtr
<IDXGIOutput3
> output3
;
242 if (FAILED(output
->QueryInterface(__uuidof(IDXGIOutput3
),
243 getter_AddRefs(output3
)))) {
247 output3
->CheckOverlaySupport(DXGI_FORMAT_NV12
, mDevice
,
248 &info
->mNv12OverlaySupportFlags
);
249 output3
->CheckOverlaySupport(DXGI_FORMAT_YUY2
, mDevice
,
250 &info
->mYuy2OverlaySupportFlags
);
251 output3
->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM
, mDevice
,
252 &info
->mBgra8OverlaySupportFlags
);
253 output3
->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM
, mDevice
,
254 &info
->mRgb10a2OverlaySupportFlags
);
256 if (FlagsSupportsOverlays(info
->mNv12OverlaySupportFlags
)) {
257 // NV12 format is preferred if it's supported.
258 info
->mOverlayFormatUsed
= DXGI_FORMAT_NV12
;
259 info
->mSupportsHardwareOverlays
= true;
262 if (!info
->mSupportsHardwareOverlays
&&
263 FlagsSupportsOverlays(info
->mYuy2OverlaySupportFlags
)) {
264 // If NV12 isn't supported, fallback to YUY2 if it's supported.
265 info
->mOverlayFormatUsed
= DXGI_FORMAT_YUY2
;
266 info
->mSupportsHardwareOverlays
= true;
269 // RGB10A2 overlay is used for displaying HDR content. In Intel's
270 // platform, RGB10A2 overlay is enabled only when
271 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
272 if (FlagsSupportsOverlays(info
->mRgb10a2OverlaySupportFlags
)) {
273 if (!CheckOverlayColorSpaceSupport(
274 DXGI_FORMAT_R10G10B10A2_UNORM
,
275 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
, output
, mDevice
))
276 info
->mRgb10a2OverlaySupportFlags
= 0;
279 // Early out after the first output that reports overlay support. All
280 // outputs are expected to report the same overlay support according to
281 // Microsoft's WDDM documentation:
282 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
283 if (info
->mSupportsHardwareOverlays
) {
288 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
289 info
->mOverlayFormatUsed
= DXGI_FORMAT_B8G8R8A8_UNORM
;
290 info
->mSupportsHardwareOverlays
= false;
293 info
->mSupportsOverlays
= info
->mSupportsHardwareOverlays
;
295 // Note: "UniquePtr::release" here is saying "release your ownership stake
296 // on your pointer, so that our StaticAutoPtr can take over ownership".
297 // (StaticAutoPtr doesn't have a move constructor that could directly steal
298 // the contents of a UniquePtr via std::move().)
299 sGpuOverlayInfo
= info
.release();
301 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
302 gpuParent
->NotifyOverlayInfo(GetOverlayInfo());
308 DCSurface
* DCLayerTree::GetSurface(wr::NativeSurfaceId aId
) const {
309 auto surface_it
= mDCSurfaces
.find(aId
);
310 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
311 return surface_it
->second
.get();
314 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1
* aSwapChain
) {
315 LOG("DCLayerTree::SetDefaultSwapChain()");
317 mRootVisual
->AddVisual(mDefaultSwapChainVisual
, TRUE
, nullptr);
318 mDefaultSwapChainVisual
->SetContent(aSwapChain
);
319 // Default SwapChain's visual does not need linear interporation.
320 mDefaultSwapChainVisual
->SetBitmapInterpolationMode(
321 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
322 mPendingCommit
= true;
325 void DCLayerTree::MaybeUpdateDebug() {
326 bool updated
= false;
327 updated
|= MaybeUpdateDebugCounter();
328 updated
|= MaybeUpdateDebugVisualRedrawRegions();
330 mPendingCommit
= true;
334 void DCLayerTree::MaybeCommit() {
335 if (!mPendingCommit
) {
338 mCompositionDevice
->Commit();
341 void DCLayerTree::WaitForCommitCompletion() {
342 mCompositionDevice
->WaitForCommitCompletion();
345 void DCLayerTree::DisableNativeCompositor() {
346 MOZ_ASSERT(mCurrentSurface
.isNothing());
347 MOZ_ASSERT(mCurrentLayers
.empty());
349 ReleaseNativeCompositorResources();
351 mRootVisual
->RemoveAllVisuals();
354 bool DCLayerTree::MaybeUpdateDebugCounter() {
355 bool debugCounter
= StaticPrefs::gfx_webrender_debug_dcomp_counter();
356 if (mDebugCounter
== debugCounter
) {
360 RefPtr
<IDCompositionDeviceDebug
> debugDevice
;
361 HRESULT hr
= mCompositionDevice
->QueryInterface(
362 (IDCompositionDeviceDebug
**)getter_AddRefs(debugDevice
));
368 debugDevice
->EnableDebugCounters();
370 debugDevice
->DisableDebugCounters();
373 mDebugCounter
= debugCounter
;
377 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
378 bool debugVisualRedrawRegions
=
379 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
380 if (mDebugVisualRedrawRegions
== debugVisualRedrawRegions
) {
384 RefPtr
<IDCompositionVisualDebug
> visualDebug
;
385 HRESULT hr
= mRootVisual
->QueryInterface(
386 (IDCompositionVisualDebug
**)getter_AddRefs(visualDebug
));
391 if (debugVisualRedrawRegions
) {
392 visualDebug
->EnableRedrawRegions();
394 visualDebug
->DisableRedrawRegions();
397 mDebugVisualRedrawRegions
= debugVisualRedrawRegions
;
401 void DCLayerTree::CompositorBeginFrame() {
403 mUsedOverlayTypesInFrame
= DCompOverlayTypes::NO_OVERLAY
;
406 void DCLayerTree::CompositorEndFrame() {
407 auto start
= TimeStamp::Now();
408 // Check if the visual tree of surfaces is the same as last frame.
409 bool same
= mPrevLayers
== mCurrentLayers
;
412 // If not, we need to rebuild the visual tree. Note that addition or
413 // removal of tiles no longer needs to rebuild the main visual tree
414 // here, since they are added as children of the surface visual.
415 mRootVisual
->RemoveAllVisuals();
418 for (auto it
= mCurrentLayers
.begin(); it
!= mCurrentLayers
.end(); ++it
) {
419 auto surface_it
= mDCSurfaces
.find(*it
);
420 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
421 const auto surface
= surface_it
->second
.get();
422 // Ensure surface is trimmed to updated tile valid rects
423 surface
->UpdateAllocatedRect();
425 // Add surfaces in z-order they were added to the scene.
426 const auto visual
= surface
->GetVisual();
427 mRootVisual
->AddVisual(visual
, false, nullptr);
431 mPrevLayers
.swap(mCurrentLayers
);
432 mCurrentLayers
.clear();
434 mCompositionDevice
->Commit();
436 auto end
= TimeStamp::Now();
437 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
438 (end
- start
).ToMilliseconds() * 10.);
440 // Remove any framebuffers that haven't been
441 // used in the last 60 frames.
443 // This should use nsTArray::RemoveElementsBy once
444 // CachedFrameBuffer is able to properly destroy
445 // itself in the destructor.
446 const auto gl
= GetGLContext();
447 for (uint32_t i
= 0, len
= mFrameBuffers
.Length(); i
< len
; ++i
) {
448 auto& fb
= mFrameBuffers
[i
];
449 if ((mCurrentFrame
- fb
.lastFrameUsed
) > 60) {
450 gl
->fDeleteRenderbuffers(1, &fb
.depthRboId
);
451 gl
->fDeleteFramebuffers(1, &fb
.fboId
);
452 mFrameBuffers
.UnorderedRemoveElementAt(i
);
453 --i
; // Examine the element again, if necessary.
458 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
462 // Disable video overlay if mCompositionDevice->Commit() with video overlay is
463 // too slow. It drops fps.
465 const auto maxCommitWaitDurationMs
= 20;
466 const auto maxSlowCommitCount
= 5;
467 const auto commitDurationMs
=
468 static_cast<uint32_t>((end
- start
).ToMilliseconds());
470 nsPrintfCString
marker("CommitWait overlay %u %ums ",
471 (uint8_t)mUsedOverlayTypesInFrame
, commitDurationMs
);
472 PROFILER_MARKER_TEXT("CommitWait", GRAPHICS
, {}, marker
);
474 if (mUsedOverlayTypesInFrame
!= DCompOverlayTypes::NO_OVERLAY
&&
475 commitDurationMs
> maxCommitWaitDurationMs
) {
478 mSlowCommitCount
= 0;
481 if (mSlowCommitCount
<= maxSlowCommitCount
) {
485 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
486 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
487 RenderThread::Get()->NotifyWebRenderError(
488 wr::WebRenderError::VIDEO_SW_OVERLAY
);
490 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::HARDWARE_DECODED_VIDEO
) {
491 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
492 RenderThread::Get()->NotifyWebRenderError(
493 wr::WebRenderError::VIDEO_HW_OVERLAY
);
497 void DCLayerTree::Bind(wr::NativeTileId aId
, wr::DeviceIntPoint
* aOffset
,
498 uint32_t* aFboId
, wr::DeviceIntRect aDirtyRect
,
499 wr::DeviceIntRect aValidRect
) {
500 auto surface
= GetSurface(aId
.surface_id
);
501 auto tile
= surface
->GetTile(aId
.x
, aId
.y
);
502 wr::DeviceIntPoint targetOffset
{0, 0};
504 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
505 // IDCompositionVirtualSurface owned by the DCSurface.
506 RefPtr
<IDCompositionSurface
> compositionSurface
;
507 if (surface
->mIsVirtualSurface
) {
508 gfx::IntRect
validRect(aValidRect
.min
.x
, aValidRect
.min
.y
,
509 aValidRect
.width(), aValidRect
.height());
510 if (!tile
->mValidRect
.IsEqualEdges(validRect
)) {
511 tile
->mValidRect
= validRect
;
512 surface
->DirtyAllocatedRect();
514 wr::DeviceIntSize tileSize
= surface
->GetTileSize();
515 compositionSurface
= surface
->GetCompositionSurface();
516 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
517 targetOffset
.x
= virtualOffset
.x
+ tileSize
.width
* aId
.x
;
518 targetOffset
.y
= virtualOffset
.y
+ tileSize
.height
* aId
.y
;
520 compositionSurface
= tile
->Bind(aValidRect
);
523 if (tile
->mNeedsFullDraw
) {
524 // dcomp requires that the first BeginDraw on a non-virtual surface is the
525 // full size of the pixel buffer.
526 auto tileSize
= surface
->GetTileSize();
527 aDirtyRect
.min
.x
= 0;
528 aDirtyRect
.min
.y
= 0;
529 aDirtyRect
.max
.x
= tileSize
.width
;
530 aDirtyRect
.max
.y
= tileSize
.height
;
531 tile
->mNeedsFullDraw
= false;
534 *aFboId
= CreateEGLSurfaceForCompositionSurface(
535 aDirtyRect
, aOffset
, compositionSurface
, targetOffset
);
536 mCurrentSurface
= Some(compositionSurface
);
539 void DCLayerTree::Unbind() {
540 if (mCurrentSurface
.isNothing()) {
544 RefPtr
<IDCompositionSurface
> surface
= mCurrentSurface
.ref();
548 mCurrentSurface
= Nothing();
551 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId
,
552 wr::DeviceIntPoint aVirtualOffset
,
553 wr::DeviceIntSize aTileSize
, bool aIsOpaque
) {
554 auto it
= mDCSurfaces
.find(aId
);
555 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
556 if (it
!= mDCSurfaces
.end()) {
557 // DCSurface already exists.
561 // Tile size needs to be positive.
562 if (aTileSize
.width
<= 0 || aTileSize
.height
<= 0) {
563 gfxCriticalNote
<< "TileSize is not positive aId: " << wr::AsUint64(aId
)
564 << " aTileSize(" << aTileSize
.width
<< ","
565 << aTileSize
.height
<< ")";
568 bool isVirtualSurface
=
569 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
570 auto surface
= MakeUnique
<DCSurface
>(aTileSize
, aVirtualOffset
,
571 isVirtualSurface
, aIsOpaque
, this);
572 if (!surface
->Initialize()) {
573 gfxCriticalNote
<< "Failed to initialize DCSurface: " << wr::AsUint64(aId
);
577 mDCSurfaces
[aId
] = std::move(surface
);
580 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId
,
582 auto it
= mDCSurfaces
.find(aId
);
583 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
585 auto surface
= MakeUnique
<DCExternalSurfaceWrapper
>(aIsOpaque
, this);
586 if (!surface
->Initialize()) {
587 gfxCriticalNote
<< "Failed to initialize DCExternalSurfaceWrapper: "
588 << wr::AsUint64(aId
);
592 mDCSurfaces
[aId
] = std::move(surface
);
595 void DCLayerTree::DestroySurface(NativeSurfaceId aId
) {
596 auto surface_it
= mDCSurfaces
.find(aId
);
597 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
598 auto surface
= surface_it
->second
.get();
600 mRootVisual
->RemoveVisual(surface
->GetVisual());
601 mDCSurfaces
.erase(surface_it
);
604 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
605 auto surface
= GetSurface(aId
);
606 surface
->CreateTile(aX
, aY
);
609 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
610 auto surface
= GetSurface(aId
);
611 surface
->DestroyTile(aX
, aY
);
614 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId
,
615 wr::ExternalImageId aExternalImage
) {
616 auto surface_it
= mDCSurfaces
.find(aId
);
617 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
618 surface_it
->second
->AttachExternalImage(aExternalImage
);
621 void DCExternalSurfaceWrapper::AttachExternalImage(
622 wr::ExternalImageId aExternalImage
) {
623 if (auto* surface
= EnsureSurfaceForExternalImage(aExternalImage
)) {
624 surface
->AttachExternalImage(aExternalImage
);
630 template <class FromT
>
631 [[nodiscard
]] static inline RefPtr
<ToT
> From(FromT
* const from
) {
633 (void)from
->QueryInterface(static_cast<ToT
**>(getter_AddRefs(to
)));
638 DCSurface
* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
639 wr::ExternalImageId aExternalImage
) {
641 return mSurface
.get();
644 // Create a new surface based on the texture type.
645 RenderTextureHost
* texture
=
646 RenderThread::Get()->GetRenderTexture(aExternalImage
);
647 if (texture
&& texture
->AsRenderDXGITextureHost()) {
648 mSurface
.reset(new DCSurfaceVideo(mIsOpaque
, mDCLayerTree
));
649 if (!mSurface
->Initialize()) {
650 gfxCriticalNote
<< "Failed to initialize DCSurfaceVideo: "
651 << wr::AsUint64(aExternalImage
);
654 } else if (texture
&& texture
->AsRenderDcompSurfaceTextureHost()) {
655 mSurface
.reset(new DCSurfaceHandle(mIsOpaque
, mDCLayerTree
));
656 if (!mSurface
->Initialize()) {
657 gfxCriticalNote
<< "Failed to initialize DCSurfaceHandle: "
658 << wr::AsUint64(aExternalImage
);
663 gfxCriticalNote
<< "Failed to create a surface for external image: "
664 << gfx::hexa(texture
);
668 // Add surface's visual which will contain video data to our root visual.
669 const auto surfaceVisual
= mSurface
->GetVisual();
670 mVisual
->AddVisual(surfaceVisual
, true, nullptr);
673 // Apply color management.
676 const auto cmsMode
= GfxColorManagementMode();
677 if (cmsMode
== CMSMode::Off
) return;
679 const auto dcomp
= mDCLayerTree
->GetCompositionDevice();
680 const auto dcomp3
= QI
<IDCompositionDevice3
>::From(dcomp
);
683 "No IDCompositionDevice3, cannot use dcomp for color management.");
689 const auto cspace
= [&]() {
690 const auto rangedCspace
= texture
->GetYUVColorSpace();
691 const auto info
= FromYUVRangedColorSpace(rangedCspace
);
692 auto ret
= ToColorSpace2(info
.space
);
693 if (ret
== gfx::ColorSpace2::Display
&& cmsMode
== CMSMode::All
) {
694 ret
= gfx::ColorSpace2::SRGB
;
699 const bool rec709GammaAsSrgb
=
700 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
701 const bool rec2020GammaAsRec709
=
702 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
704 auto cspaceDesc
= color::ColorspaceDesc
{};
706 case gfx::ColorSpace2::Display
:
707 return; // No color management needed!
708 case gfx::ColorSpace2::SRGB
:
709 cspaceDesc
.chrom
= color::Chromaticities::Srgb();
710 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
713 case gfx::ColorSpace2::DISPLAY_P3
:
714 cspaceDesc
.chrom
= color::Chromaticities::DisplayP3();
715 cspaceDesc
.tf
= color::PiecewiseGammaDesc::DisplayP3();
718 case gfx::ColorSpace2::BT601_525
:
719 cspaceDesc
.chrom
= color::Chromaticities::Rec601_525_Ntsc();
720 if (rec709GammaAsSrgb
) {
721 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
723 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
727 case gfx::ColorSpace2::BT709
:
728 cspaceDesc
.chrom
= color::Chromaticities::Rec709();
729 if (rec709GammaAsSrgb
) {
730 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
732 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
736 case gfx::ColorSpace2::BT2020
:
737 cspaceDesc
.chrom
= color::Chromaticities::Rec2020();
738 if (rec2020GammaAsRec709
&& rec709GammaAsSrgb
) {
739 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
740 } else if (rec2020GammaAsRec709
) {
741 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
743 // Just Rec709 with slightly more precision.
744 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec2020_12bit();
749 const auto cprofileIn
= color::ColorProfileDesc::From(cspaceDesc
);
750 auto cprofileOut
= mDCLayerTree
->OutputColorProfile();
751 bool pretendSrgb
= true;
753 cprofileOut
= color::ColorProfileDesc::From({
754 color::Chromaticities::Srgb(),
755 color::PiecewiseGammaDesc::Srgb(),
758 const auto conversion
= color::ColorProfileConversionDesc::From({
765 auto chain
= ColorManagementChain::From(*dcomp3
, conversion
);
766 mCManageChain
= Some(chain
);
768 surfaceVisual
->SetEffect(mCManageChain
->last
.get());
771 return mSurface
.get();
774 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix
& aTransform
) {
775 MOZ_ASSERT(mSurface
);
776 if (auto* surface
= mSurface
->AsDCSurfaceVideo()) {
777 if (surface
->CalculateSwapChainSize(aTransform
)) {
778 surface
->PresentVideo();
780 } else if (auto* surface
= mSurface
->AsDCSurfaceHandle()) {
781 surface
->PresentSurfaceHandle();
785 template <typename T
>
786 static inline D2D1_RECT_F
D2DRect(const T
& aRect
) {
787 return D2D1::RectF(aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost());
790 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const gfx::Matrix
& aTransform
) {
791 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
, aTransform
._21
,
792 aTransform
._22
, aTransform
._31
, aTransform
._32
);
795 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId
,
796 const wr::CompositorSurfaceTransform
& aTransform
,
797 wr::DeviceIntRect aClipRect
,
798 wr::ImageRendering aImageRendering
) {
799 auto it
= mDCSurfaces
.find(aId
);
800 MOZ_RELEASE_ASSERT(it
!= mDCSurfaces
.end());
801 const auto surface
= it
->second
.get();
802 const auto visual
= surface
->GetVisual();
804 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
806 float sx
= aTransform
.scale
.x
;
807 float sy
= aTransform
.scale
.y
;
808 float tx
= aTransform
.offset
.x
;
809 float ty
= aTransform
.offset
.y
;
810 gfx::Matrix
transform(sx
, 0.0, 0.0, sy
, tx
, ty
);
812 surface
->PresentExternalSurface(transform
);
814 transform
.PreTranslate(-virtualOffset
.x
, -virtualOffset
.y
);
816 // The DirectComposition API applies clipping *before* any
817 // transforms/offset, whereas we want the clip applied after. Right now, we
818 // only support rectilinear transforms, and then we transform our clip into
819 // pre-transform coordinate space for it to be applied there.
820 // DirectComposition does have an option for pre-transform clipping, if you
821 // create an explicit IDCompositionEffectGroup object and set a 3D transform
822 // on that. I suspect that will perform worse though, so we should only do
823 // that for complex transforms (which are never provided right now).
824 MOZ_ASSERT(transform
.IsRectilinear());
825 gfx::Rect clip
= transform
.Inverse().TransformBounds(gfx::Rect(
826 aClipRect
.min
.x
, aClipRect
.min
.y
, aClipRect
.width(), aClipRect
.height()));
827 // Set the clip rect - converting from world space to the pre-offset space
828 // that DC requires for rectangle clips.
829 visual
->SetClip(D2DRect(clip
));
831 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
832 // the D3D API level (unless we QI to IDCompositionVisual3, which might
833 // not be available?).
834 // Should we assert here, or restrict at the WR API level.
835 visual
->SetTransform(D2DMatrix(transform
));
837 if (aImageRendering
== wr::ImageRendering::Auto
) {
838 visual
->SetBitmapInterpolationMode(
839 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR
);
841 visual
->SetBitmapInterpolationMode(
842 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
845 mCurrentLayers
.push_back(aId
);
848 GLuint
DCLayerTree::GetOrCreateFbo(int aWidth
, int aHeight
) {
849 const auto gl
= GetGLContext();
852 // Check if we have a cached FBO with matching dimensions
853 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
854 if (it
->width
== aWidth
&& it
->height
== aHeight
) {
856 it
->lastFrameUsed
= mCurrentFrame
;
861 // If not, create a new FBO with attached depth buffer
863 // Create the depth buffer
865 gl
->fGenRenderbuffers(1, &depthRboId
);
866 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, depthRboId
);
867 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, LOCAL_GL_DEPTH_COMPONENT24
,
870 // Create the framebuffer and attach the depth buffer to it
871 gl
->fGenFramebuffers(1, &fboId
);
872 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
873 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
874 LOCAL_GL_DEPTH_ATTACHMENT
,
875 LOCAL_GL_RENDERBUFFER
, depthRboId
);
877 // Store this in the cache for future calls.
878 // TODO(gw): Maybe we should periodically scan this list and remove old
880 // haven't been used for some time?
881 DCLayerTree::CachedFrameBuffer frame_buffer_info
;
882 frame_buffer_info
.width
= aWidth
;
883 frame_buffer_info
.height
= aHeight
;
884 frame_buffer_info
.fboId
= fboId
;
885 frame_buffer_info
.depthRboId
= depthRboId
;
886 frame_buffer_info
.lastFrameUsed
= mCurrentFrame
;
887 mFrameBuffers
.AppendElement(frame_buffer_info
);
893 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize
& aInputSize
,
894 const gfx::IntSize
& aOutputSize
) {
897 if (!mVideoDevice
|| !mVideoContext
) {
901 if (mVideoProcessor
&& (aInputSize
<= mVideoInputSize
) &&
902 (aOutputSize
<= mVideoOutputSize
)) {
906 mVideoProcessor
= nullptr;
907 mVideoProcessorEnumerator
= nullptr;
909 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc
= {};
910 desc
.InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE
;
911 desc
.InputFrameRate
.Numerator
= 60;
912 desc
.InputFrameRate
.Denominator
= 1;
913 desc
.InputWidth
= aInputSize
.width
;
914 desc
.InputHeight
= aInputSize
.height
;
915 desc
.OutputFrameRate
.Numerator
= 60;
916 desc
.OutputFrameRate
.Denominator
= 1;
917 desc
.OutputWidth
= aOutputSize
.width
;
918 desc
.OutputHeight
= aOutputSize
.height
;
919 desc
.Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
;
921 hr
= mVideoDevice
->CreateVideoProcessorEnumerator(
922 &desc
, getter_AddRefs(mVideoProcessorEnumerator
));
924 gfxCriticalNote
<< "Failed to create VideoProcessorEnumerator: "
929 hr
= mVideoDevice
->CreateVideoProcessor(mVideoProcessorEnumerator
, 0,
930 getter_AddRefs(mVideoProcessor
));
932 mVideoProcessor
= nullptr;
933 mVideoProcessorEnumerator
= nullptr;
934 gfxCriticalNote
<< "Failed to create VideoProcessor: " << gfx::hexa(hr
);
938 // Reduce power cosumption
939 // By default, the driver might perform certain processing tasks
941 mVideoContext
->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor
, 0,
944 mVideoInputSize
= aInputSize
;
945 mVideoOutputSize
= aOutputSize
;
950 bool DCLayerTree::SupportsHardwareOverlays() {
951 return sGpuOverlayInfo
->mSupportsHardwareOverlays
;
954 bool DCLayerTree::SupportsSwapChainTearing() {
955 RefPtr
<ID3D11Device
> device
= mDevice
;
956 static const bool supported
= [device
] {
957 RefPtr
<IDXGIDevice
> dxgiDevice
;
958 RefPtr
<IDXGIAdapter
> adapter
;
959 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
960 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
962 RefPtr
<IDXGIFactory5
> dxgiFactory
;
963 HRESULT hr
= adapter
->GetParent(
964 IID_PPV_ARGS((IDXGIFactory5
**)getter_AddRefs(dxgiFactory
)));
969 BOOL presentAllowTearing
= FALSE
;
970 hr
= dxgiFactory
->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING
,
971 &presentAllowTearing
,
972 sizeof(presentAllowTearing
));
977 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
978 gpuParent
->NotifySwapChainInfo(
979 layers::SwapChainInfo(!!presentAllowTearing
));
980 } else if (XRE_IsParentProcess()) {
981 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
983 return !!presentAllowTearing
;
988 DXGI_FORMAT
DCLayerTree::GetOverlayFormatForSDR() {
989 return sGpuOverlayInfo
->mOverlayFormatUsed
;
992 static layers::OverlaySupportType
FlagsToOverlaySupportType(
993 UINT aFlags
, bool aSoftwareOverlaySupported
) {
994 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_SCALING
) {
995 return layers::OverlaySupportType::Scaling
;
997 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
) {
998 return layers::OverlaySupportType::Direct
;
1000 if (aSoftwareOverlaySupported
) {
1001 return layers::OverlaySupportType::Software
;
1003 return layers::OverlaySupportType::None
;
1006 layers::OverlayInfo
DCLayerTree::GetOverlayInfo() {
1007 layers::OverlayInfo info
;
1009 info
.mSupportsOverlays
= sGpuOverlayInfo
->mSupportsHardwareOverlays
;
1011 FlagsToOverlaySupportType(sGpuOverlayInfo
->mNv12OverlaySupportFlags
,
1012 /* aSoftwareOverlaySupported */ false);
1014 FlagsToOverlaySupportType(sGpuOverlayInfo
->mYuy2OverlaySupportFlags
,
1015 /* aSoftwareOverlaySupported */ false);
1016 info
.mBgra8Overlay
=
1017 FlagsToOverlaySupportType(sGpuOverlayInfo
->mBgra8OverlaySupportFlags
,
1018 /* aSoftwareOverlaySupported */ true);
1019 info
.mRgb10a2Overlay
=
1020 FlagsToOverlaySupportType(sGpuOverlayInfo
->mRgb10a2OverlaySupportFlags
,
1021 /* aSoftwareOverlaySupported */ false);
1026 void DCLayerTree::SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes
) {
1027 mUsedOverlayTypesInFrame
|= aTypes
;
1030 DCSurface::DCSurface(wr::DeviceIntSize aTileSize
,
1031 wr::DeviceIntPoint aVirtualOffset
, bool aIsVirtualSurface
,
1032 bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1033 : mIsVirtualSurface(aIsVirtualSurface
),
1034 mDCLayerTree(aDCLayerTree
),
1035 mTileSize(aTileSize
),
1036 mIsOpaque(aIsOpaque
),
1037 mAllocatedRectDirty(true),
1038 mVirtualOffset(aVirtualOffset
) {}
1040 DCSurface::~DCSurface() {}
1042 bool DCSurface::Initialize() {
1043 // Create a visual for tiles to attach to, whether virtual or not.
1045 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1046 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1048 gfxCriticalNote
<< "Failed to create DCompositionVisual: " << gfx::hexa(hr
);
1052 // If virtual surface is enabled, create and attach to visual, in this case
1053 // the tiles won't own visuals or surfaces.
1054 if (mIsVirtualSurface
) {
1055 DXGI_ALPHA_MODE alpha_mode
=
1056 mIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1058 hr
= dCompDevice
->CreateVirtualSurface(
1059 VIRTUAL_SURFACE_SIZE
, VIRTUAL_SURFACE_SIZE
, DXGI_FORMAT_R8G8B8A8_UNORM
,
1060 alpha_mode
, getter_AddRefs(mVirtualSurface
));
1061 MOZ_ASSERT(SUCCEEDED(hr
));
1063 // Bind the surface memory to this visual
1064 hr
= mVisual
->SetContent(mVirtualSurface
);
1065 MOZ_ASSERT(SUCCEEDED(hr
));
1071 void DCSurface::CreateTile(int32_t aX
, int32_t aY
) {
1072 TileKey
key(aX
, aY
);
1073 MOZ_RELEASE_ASSERT(mDCTiles
.find(key
) == mDCTiles
.end());
1075 auto tile
= MakeUnique
<DCTile
>(mDCLayerTree
);
1076 if (!tile
->Initialize(aX
, aY
, mTileSize
, mIsVirtualSurface
, mIsOpaque
,
1078 gfxCriticalNote
<< "Failed to initialize DCTile: " << aX
<< aY
;
1082 if (mIsVirtualSurface
) {
1083 mAllocatedRectDirty
= true;
1085 mVisual
->AddVisual(tile
->GetVisual(), false, nullptr);
1088 mDCTiles
[key
] = std::move(tile
);
1091 void DCSurface::DestroyTile(int32_t aX
, int32_t aY
) {
1092 TileKey
key(aX
, aY
);
1093 if (mIsVirtualSurface
) {
1094 mAllocatedRectDirty
= true;
1096 auto tile
= GetTile(aX
, aY
);
1097 mVisual
->RemoveVisual(tile
->GetVisual());
1099 mDCTiles
.erase(key
);
1102 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty
= true; }
1104 void DCSurface::UpdateAllocatedRect() {
1105 if (mAllocatedRectDirty
) {
1106 if (mVirtualSurface
) {
1107 // The virtual surface may have holes in it (for example, an empty tile
1108 // that has no primitives). Instead of trimming to a single bounding
1109 // rect, supply the rect of each valid tile to handle this case.
1110 std::vector
<RECT
> validRects
;
1112 for (auto it
= mDCTiles
.begin(); it
!= mDCTiles
.end(); ++it
) {
1113 auto tile
= GetTile(it
->first
.mX
, it
->first
.mY
);
1116 rect
.left
= (LONG
)(mVirtualOffset
.x
+ it
->first
.mX
* mTileSize
.width
+
1117 tile
->mValidRect
.x
);
1118 rect
.top
= (LONG
)(mVirtualOffset
.y
+ it
->first
.mY
* mTileSize
.height
+
1119 tile
->mValidRect
.y
);
1120 rect
.right
= rect
.left
+ tile
->mValidRect
.width
;
1121 rect
.bottom
= rect
.top
+ tile
->mValidRect
.height
;
1123 validRects
.push_back(rect
);
1126 mVirtualSurface
->Trim(validRects
.data(), validRects
.size());
1128 // When not using a virtual surface, we still want to reset this
1129 mAllocatedRectDirty
= false;
1133 DCTile
* DCSurface::GetTile(int32_t aX
, int32_t aY
) const {
1134 TileKey
key(aX
, aY
);
1135 auto tile_it
= mDCTiles
.find(key
);
1136 MOZ_RELEASE_ASSERT(tile_it
!= mDCTiles
.end());
1137 return tile_it
->second
.get();
1140 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1141 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1143 mSwapChainBufferCount(
1144 StaticPrefs::gfx_webrender_dcomp_video_force_triple_buffering() ? 3
1148 DCSurfaceVideo::~DCSurfaceVideo() {
1149 ReleaseDecodeSwapChainResources();
1150 MOZ_ASSERT(!mSwapChainSurfaceHandle
);
1153 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat
) {
1154 if (aFormat
== DXGI_FORMAT_NV12
|| aFormat
== DXGI_FORMAT_YUY2
) {
1160 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1161 RenderTextureHost
* texture
=
1162 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1163 MOZ_RELEASE_ASSERT(texture
);
1165 if (mPrevTexture
== texture
) {
1169 // XXX if software decoded video frame format is nv12, it could be used as
1171 if (!texture
|| !texture
->AsRenderDXGITextureHost() ||
1172 texture
->GetFormat() != gfx::SurfaceFormat::NV12
) {
1173 gfxCriticalNote
<< "Unsupported RenderTexture for overlay: "
1174 << gfx::hexa(texture
);
1178 mRenderTextureHost
= texture
;
1181 static UINT
GetVendorId(ID3D11VideoDevice
* const aVideoDevice
) {
1182 RefPtr
<IDXGIDevice
> dxgiDevice
;
1183 RefPtr
<IDXGIAdapter
> adapter
;
1184 aVideoDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1185 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1187 DXGI_ADAPTER_DESC adapterDesc
;
1188 adapter
->GetDesc(&adapterDesc
);
1190 return adapterDesc
.VendorId
;
1193 static HRESULT
SetNvidiaVpSuperResolution(ID3D11VideoContext
* aVideoContext
,
1194 ID3D11VideoProcessor
* aVideoProcessor
,
1196 LOG("SetNvidiaVpSuperResolution() aEnable=%d", aEnable
);
1198 // Undocumented NVIDIA driver constants
1199 constexpr GUID nvGUID
= {0xD43CE1B3,
1202 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
1204 constexpr UINT nvExtensionVersion
= 0x1;
1205 constexpr UINT nvExtensionMethodSuperResolution
= 0x2;
1210 } streamExtensionInfo
= {nvExtensionVersion
, nvExtensionMethodSuperResolution
,
1214 hr
= aVideoContext
->VideoProcessorSetStreamExtension(
1215 aVideoProcessor
, 0, &nvGUID
, sizeof(streamExtensionInfo
),
1216 &streamExtensionInfo
);
1220 static HRESULT
SetVpSuperResolution(UINT aGpuVendorId
,
1221 ID3D11VideoContext
* aVideoContext
,
1222 ID3D11VideoProcessor
* aVideoProcessor
,
1224 MOZ_ASSERT(aVideoContext
);
1225 MOZ_ASSERT(aVideoProcessor
);
1227 if (aGpuVendorId
== 0x10DE) {
1228 return SetNvidiaVpSuperResolution(aVideoContext
, aVideoProcessor
, aEnable
);
1233 static bool GetNvidiaRTXVideoTrueHDRSupported(
1234 ID3D11VideoContext
* aVideoContext
, ID3D11VideoProcessor
* aVideoProcessor
) {
1235 const GUID kNvidiaTrueHDRInterfaceGUID
= {
1239 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
1241 HRESULT hr
= aVideoContext
->VideoProcessorGetStreamExtension(
1242 aVideoProcessor
, 0, &kNvidiaTrueHDRInterfaceGUID
, sizeof(available
),
1248 bool driverSupportsTrueHdr
= (available
== 1);
1249 return driverSupportsTrueHdr
;
1252 static HRESULT
SetNvidiaRTXVideoTrueHDR(ID3D11VideoContext
* aVideoContext
,
1253 ID3D11VideoProcessor
* aVideoProcessor
,
1255 constexpr GUID kNvidiaTrueHDRInterfaceGUID
= {
1259 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
1260 constexpr UINT kStreamExtensionMethodTrueHDR
= 0x3;
1261 const UINT TrueHDRVersion4
= 4;
1267 } streamExtensionInfo
= {TrueHDRVersion4
, kStreamExtensionMethodTrueHDR
,
1268 aEnable
? 1u : 0u, 0u};
1269 HRESULT hr
= aVideoContext
->VideoProcessorSetStreamExtension(
1270 aVideoProcessor
, 0, &kNvidiaTrueHDRInterfaceGUID
,
1271 sizeof(streamExtensionInfo
), &streamExtensionInfo
);
1275 static bool GetVpAutoHDRSupported(UINT aGpuVendorId
,
1276 ID3D11VideoContext
* aVideoContext
,
1277 ID3D11VideoProcessor
* aVideoProcessor
) {
1278 MOZ_ASSERT(aVideoContext
);
1279 MOZ_ASSERT(aVideoProcessor
);
1281 if (aGpuVendorId
== 0x10DE) {
1282 return GetNvidiaRTXVideoTrueHDRSupported(aVideoContext
, aVideoProcessor
);
1287 static HRESULT
SetVpAutoHDR(UINT aGpuVendorId
,
1288 ID3D11VideoContext
* aVideoContext
,
1289 ID3D11VideoProcessor
* aVideoProcessor
,
1291 MOZ_ASSERT(aVideoContext
);
1292 MOZ_ASSERT(aVideoProcessor
);
1294 if (aGpuVendorId
== 0x10DE) {
1295 return SetNvidiaRTXVideoTrueHDR(aVideoContext
, aVideoProcessor
, aEnable
);
1297 MOZ_ASSERT_UNREACHABLE("Unexpected to be called");
1301 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix
& aTransform
) {
1302 if (!mRenderTextureHost
) {
1303 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1307 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1308 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1309 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1310 mDCLayerTree
->SetUsedOverlayTypeInFrame(overlayType
);
1312 mVideoSize
= mRenderTextureHost
->AsRenderDXGITextureHost()->GetSize(0);
1314 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1315 // DCSurfaceVideo::PresentVideo() needs to be called.
1316 bool needsToPresent
= mPrevTexture
!= mRenderTextureHost
;
1317 gfx::IntSize swapChainSize
= mVideoSize
;
1318 gfx::Matrix transform
= aTransform
;
1319 const bool isDRM
= mRenderTextureHost
->IsFromDRMSource();
1321 // When video is rendered to axis aligned integer rectangle, video scaling
1322 // could be done by VideoProcessor
1323 bool scaleVideoAtVideoProcessor
= false;
1324 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1325 aTransform
.PreservesAxisAlignedRectangles()) {
1326 gfx::Size scaledSize
= gfx::Size(mVideoSize
) * aTransform
.ScaleFactors();
1327 gfx::IntSize
size(int32_t(std::round(scaledSize
.width
)),
1328 int32_t(std::round(scaledSize
.height
)));
1329 if (gfx::FuzzyEqual(scaledSize
.width
, size
.width
, 0.1f
) &&
1330 gfx::FuzzyEqual(scaledSize
.height
, size
.height
, 0.1f
)) {
1331 scaleVideoAtVideoProcessor
= true;
1332 swapChainSize
= size
;
1336 if (scaleVideoAtVideoProcessor
) {
1337 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1338 // subsampled formats like NV12 must have an even width and height.
1339 if (swapChainSize
.width
% 2 == 1) {
1340 swapChainSize
.width
+= 1;
1342 if (swapChainSize
.height
% 2 == 1) {
1343 swapChainSize
.height
+= 1;
1345 transform
= gfx::Matrix::Translation(aTransform
.GetTranslation());
1348 if (!mDCLayerTree
->EnsureVideoProcessor(mVideoSize
, swapChainSize
)) {
1349 gfxCriticalNote
<< "EnsureVideoProcessor Failed";
1353 MOZ_ASSERT(mDCLayerTree
->GetVideoContext());
1354 MOZ_ASSERT(mDCLayerTree
->GetVideoProcessor());
1356 const UINT vendorId
= GetVendorId(mDCLayerTree
->GetVideoDevice());
1357 const bool driverSupportsTrueHDR
=
1358 GetVpAutoHDRSupported(vendorId
, mDCLayerTree
->GetVideoContext(),
1359 mDCLayerTree
->GetVideoProcessor());
1360 const bool contentIsHDR
= false; // XXX for now, only non-HDR is supported.
1361 const bool monitorIsHDR
= gfx::DeviceManagerDx::Get()->SystemHDREnabled();
1362 const bool powerIsCharging
= RenderThread::Get()->GetPowerIsCharging();
1364 bool useVpAutoHDR
= gfx::gfxVars::WebRenderOverlayVpAutoHDR() &&
1365 !contentIsHDR
&& monitorIsHDR
&& driverSupportsTrueHDR
&&
1366 powerIsCharging
&& !mVpAutoHDRFailed
;
1368 if (!mVideoSwapChain
|| mSwapChainSize
!= swapChainSize
|| mIsDRM
!= isDRM
||
1369 mUseVpAutoHDR
!= useVpAutoHDR
) {
1370 needsToPresent
= true;
1371 ReleaseDecodeSwapChainResources();
1372 // Update mSwapChainSize before creating SwapChain
1373 mSwapChainSize
= swapChainSize
;
1376 auto swapChainFormat
= GetSwapChainFormat(useVpAutoHDR
);
1377 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1378 if (useYUVSwapChain
) {
1379 // Tries to create YUV SwapChain
1380 CreateVideoSwapChain(swapChainFormat
);
1381 if (!mVideoSwapChain
) {
1382 mFailedYuvSwapChain
= true;
1383 ReleaseDecodeSwapChainResources();
1385 gfxCriticalNote
<< "Fallback to RGB SwapChain";
1388 // Tries to create RGB SwapChain
1389 if (!mVideoSwapChain
) {
1390 CreateVideoSwapChain(swapChainFormat
);
1392 if (!mVideoSwapChain
&& useVpAutoHDR
) {
1393 mVpAutoHDRFailed
= true;
1394 gfxCriticalNoteOnce
<< "Failed to create video SwapChain for VpAutoHDR";
1396 // Disable VpAutoHDR
1397 useVpAutoHDR
= false;
1398 swapChainFormat
= GetSwapChainFormat(useVpAutoHDR
);
1399 CreateVideoSwapChain(swapChainFormat
);
1403 aTransform
= transform
;
1404 mUseVpAutoHDR
= useVpAutoHDR
;
1406 return needsToPresent
;
1409 void DCSurfaceVideo::PresentVideo() {
1410 if (!mRenderTextureHost
) {
1414 if (!mVideoSwapChain
) {
1415 gfxCriticalNote
<< "Failed to create VideoSwapChain";
1416 RenderThread::Get()->NotifyWebRenderError(
1417 wr::WebRenderError::VIDEO_OVERLAY
);
1421 mVisual
->SetContent(mVideoSwapChain
);
1423 if (!CallVideoProcessorBlt()) {
1424 bool useYUVSwapChain
= IsYUVSwapChainFormat(mSwapChainFormat
);
1425 if (useYUVSwapChain
) {
1426 mFailedYuvSwapChain
= true;
1427 ReleaseDecodeSwapChainResources();
1430 RenderThread::Get()->NotifyWebRenderError(
1431 wr::WebRenderError::VIDEO_OVERLAY
);
1435 const auto device
= mDCLayerTree
->GetDevice();
1437 if (mFirstPresent
) {
1438 mFirstPresent
= false;
1439 UINT flags
= DXGI_PRESENT_USE_DURATION
;
1440 // DirectComposition can display black for a swap chain between the first
1441 // and second time it's presented to - maybe the first Present can get lost
1442 // somehow and it shows the wrong buffer. In that case copy the buffers so
1443 // all have the correct contents, which seems to help. The first Present()
1444 // after this needs to have SyncInterval > 0, or else the workaround doesn't
1446 for (size_t i
= 0; i
< mSwapChainBufferCount
- 1; ++i
) {
1447 hr
= mVideoSwapChain
->Present(0, flags
);
1448 // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only
1449 // indicates that the window is occluded and we can stop rendering.
1450 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1451 gfxCriticalNoteOnce
<< "video Present failed during first present: "
1456 RefPtr
<ID3D11Texture2D
> destTexture
;
1457 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1458 (void**)getter_AddRefs(destTexture
));
1459 MOZ_ASSERT(destTexture
);
1460 RefPtr
<ID3D11Texture2D
> srcTexture
;
1461 hr
= mVideoSwapChain
->GetBuffer(1, __uuidof(ID3D11Texture2D
),
1462 (void**)getter_AddRefs(srcTexture
));
1463 MOZ_ASSERT(srcTexture
);
1464 RefPtr
<ID3D11DeviceContext
> context
;
1465 device
->GetImmediateContext(getter_AddRefs(context
));
1466 MOZ_ASSERT(context
);
1467 context
->CopyResource(destTexture
, srcTexture
);
1470 // Additionally wait for the GPU to finish executing its commands, or
1471 // there still may be a black flicker when presenting expensive content
1474 RefPtr
<IDXGIDevice2
> dxgiDevice2
;
1475 device
->QueryInterface((IDXGIDevice2
**)getter_AddRefs(dxgiDevice2
));
1476 MOZ_ASSERT(dxgiDevice2
);
1478 HANDLE event
= ::CreateEvent(nullptr, false, false, nullptr);
1479 hr
= dxgiDevice2
->EnqueueSetEvent(event
);
1480 if (SUCCEEDED(hr
)) {
1481 DebugOnly
<DWORD
> result
= ::WaitForSingleObject(event
, INFINITE
);
1482 MOZ_ASSERT(result
== WAIT_OBJECT_0
);
1484 gfxCriticalNoteOnce
<< "EnqueueSetEvent failed: " << gfx::hexa(hr
);
1486 ::CloseHandle(event
);
1489 UINT flags
= DXGI_PRESENT_USE_DURATION
;
1491 if (StaticPrefs::gfx_webrender_dcomp_video_swap_chain_present_interval_0()) {
1495 auto start
= TimeStamp::Now();
1496 hr
= mVideoSwapChain
->Present(interval
, flags
);
1497 auto end
= TimeStamp::Now();
1499 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1500 gfxCriticalNoteOnce
<< "video Present failed: " << gfx::hexa(hr
);
1503 mPrevTexture
= mRenderTextureHost
;
1505 // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops
1508 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
1512 const auto maxPresentWaitDurationMs
= 2;
1513 const auto maxSlowPresentCount
= 5;
1514 const auto presentDurationMs
=
1515 static_cast<uint32_t>((end
- start
).ToMilliseconds());
1516 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1517 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1518 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1520 nsPrintfCString
marker("PresentWait overlay %u %ums ", (uint8_t)overlayType
,
1522 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS
, {}, marker
);
1524 if (presentDurationMs
> maxPresentWaitDurationMs
) {
1525 mSlowPresentCount
++;
1527 mSlowPresentCount
= 0;
1530 if (mSlowPresentCount
<= maxSlowPresentCount
) {
1534 if (overlayType
== DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
1535 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
1536 RenderThread::Get()->NotifyWebRenderError(
1537 wr::WebRenderError::VIDEO_SW_OVERLAY
);
1539 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
1540 RenderThread::Get()->NotifyWebRenderError(
1541 wr::WebRenderError::VIDEO_HW_OVERLAY
);
1545 DXGI_FORMAT
DCSurfaceVideo::GetSwapChainFormat(bool aUseVpAutoHDR
) {
1546 if (aUseVpAutoHDR
) {
1547 return DXGI_FORMAT_R16G16B16A16_FLOAT
;
1549 if (mFailedYuvSwapChain
|| !mDCLayerTree
->SupportsHardwareOverlays()) {
1550 return DXGI_FORMAT_B8G8R8A8_UNORM
;
1552 return mDCLayerTree
->GetOverlayFormatForSDR();
1555 bool DCSurfaceVideo::CreateVideoSwapChain(DXGI_FORMAT aSwapChainFormat
) {
1556 MOZ_ASSERT(mRenderTextureHost
);
1558 mFirstPresent
= true;
1560 const auto device
= mDCLayerTree
->GetDevice();
1562 RefPtr
<IDXGIDevice
> dxgiDevice
;
1563 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1565 RefPtr
<IDXGIFactoryMedia
> dxgiFactoryMedia
;
1567 RefPtr
<IDXGIAdapter
> adapter
;
1568 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1570 IID_PPV_ARGS((IDXGIFactoryMedia
**)getter_AddRefs(dxgiFactoryMedia
)));
1573 mSwapChainSurfaceHandle
= gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1574 if (!mSwapChainSurfaceHandle
) {
1575 gfxCriticalNote
<< "Failed to create DCompSurfaceHandle";
1579 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1580 desc
.Width
= mSwapChainSize
.width
;
1581 desc
.Height
= mSwapChainSize
.height
;
1582 desc
.Format
= aSwapChainFormat
;
1583 desc
.Stereo
= FALSE
;
1584 desc
.SampleDesc
.Count
= 1;
1585 desc
.BufferCount
= mSwapChainBufferCount
;
1586 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1587 desc
.Scaling
= DXGI_SCALING_STRETCH
;
1588 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1589 desc
.Flags
= DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO
;
1590 if (IsYUVSwapChainFormat(aSwapChainFormat
)) {
1591 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO
;
1594 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY
;
1596 desc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
1599 hr
= dxgiFactoryMedia
->CreateSwapChainForCompositionSurfaceHandle(
1600 device
, mSwapChainSurfaceHandle
, &desc
, nullptr,
1601 getter_AddRefs(mVideoSwapChain
));
1604 gfxCriticalNote
<< "Failed to create video SwapChain: " << gfx::hexa(hr
)
1605 << " " << mSwapChainSize
;
1609 mSwapChainFormat
= aSwapChainFormat
;
1613 // TODO: Replace with YUVRangedColorSpace
1614 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1615 const gfx::YUVColorSpace aYUVColorSpace
,
1616 const gfx::ColorRange aColorRange
) {
1617 if (aYUVColorSpace
== gfx::YUVColorSpace::BT601
) {
1618 if (aColorRange
== gfx::ColorRange::FULL
) {
1619 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601
);
1621 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
);
1623 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT709
) {
1624 if (aColorRange
== gfx::ColorRange::FULL
) {
1625 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
);
1627 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
);
1629 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT2020
) {
1630 if (aColorRange
== gfx::ColorRange::FULL
) {
1631 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1633 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020
);
1635 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
);
1642 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1643 const gfx::YUVRangedColorSpace aYUVColorSpace
) {
1644 const auto info
= FromYUVRangedColorSpace(aYUVColorSpace
);
1645 return GetSourceDXGIColorSpace(info
.space
, info
.range
);
1648 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1649 MOZ_ASSERT(mRenderTextureHost
);
1652 const auto videoDevice
= mDCLayerTree
->GetVideoDevice();
1653 const auto videoContext
= mDCLayerTree
->GetVideoContext();
1654 const auto texture
= mRenderTextureHost
->AsRenderDXGITextureHost();
1656 Maybe
<DXGI_COLOR_SPACE_TYPE
> sourceColorSpace
=
1657 GetSourceDXGIColorSpace(texture
->GetYUVColorSpace());
1658 if (sourceColorSpace
.isNothing()) {
1659 gfxCriticalNote
<< "Unsupported color space";
1663 RefPtr
<ID3D11Texture2D
> texture2D
= texture
->GetD3D11Texture2DWithGL();
1665 gfxCriticalNote
<< "Failed to get D3D11Texture2D";
1669 if (!mVideoSwapChain
) {
1673 auto query
= texture
->GetQuery();
1675 // Wait ID3D11Query of D3D11Texture2D copy complete just before blitting for
1676 // video overlay with non Intel GPUs. See Bug 1817617.
1678 bool ret
= layers::WaitForFrameGPUQuery(mDCLayerTree
->GetDevice(),
1679 mDCLayerTree
->GetDeviceContext(),
1682 gfxCriticalNoteOnce
<< "WaitForFrameGPUQuery() failed";
1686 RefPtr
<IDXGISwapChain3
> swapChain3
;
1687 mVideoSwapChain
->QueryInterface(
1688 (IDXGISwapChain3
**)getter_AddRefs(swapChain3
));
1690 gfxCriticalNote
<< "Failed to get IDXGISwapChain3";
1694 RefPtr
<ID3D11VideoContext1
> videoContext1
;
1695 videoContext
->QueryInterface(
1696 (ID3D11VideoContext1
**)getter_AddRefs(videoContext1
));
1697 if (!videoContext1
) {
1698 gfxCriticalNote
<< "Failed to get ID3D11VideoContext1";
1702 const auto videoProcessor
= mDCLayerTree
->GetVideoProcessor();
1703 const auto videoProcessorEnumerator
=
1704 mDCLayerTree
->GetVideoProcessorEnumerator();
1706 DXGI_COLOR_SPACE_TYPE inputColorSpace
= sourceColorSpace
.ref();
1707 videoContext1
->VideoProcessorSetStreamColorSpace1(videoProcessor
, 0,
1710 DXGI_COLOR_SPACE_TYPE outputColorSpace
=
1711 IsYUVSwapChainFormat(mSwapChainFormat
)
1713 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1715 if (mUseVpAutoHDR
) {
1716 outputColorSpace
= mSwapChainFormat
== DXGI_FORMAT_R16G16B16A16_FLOAT
1717 ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709
1718 : DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
;
1721 hr
= swapChain3
->SetColorSpace1(outputColorSpace
);
1723 gfxCriticalNoteOnce
<< "SetColorSpace1 failed: " << gfx::hexa(hr
);
1724 RenderThread::Get()->NotifyWebRenderError(
1725 wr::WebRenderError::VIDEO_OVERLAY
);
1728 videoContext1
->VideoProcessorSetOutputColorSpace1(videoProcessor
,
1731 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc
= {};
1732 inputDesc
.ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
;
1733 inputDesc
.Texture2D
.ArraySlice
= texture
->ArrayIndex();
1735 RefPtr
<ID3D11VideoProcessorInputView
> inputView
;
1736 hr
= videoDevice
->CreateVideoProcessorInputView(
1737 texture2D
, videoProcessorEnumerator
, &inputDesc
,
1738 getter_AddRefs(inputView
));
1740 gfxCriticalNote
<< "ID3D11VideoProcessorInputView creation failed: "
1745 D3D11_VIDEO_PROCESSOR_STREAM stream
= {};
1746 stream
.Enable
= true;
1747 stream
.OutputIndex
= 0;
1748 stream
.InputFrameOrField
= 0;
1749 stream
.PastFrames
= 0;
1750 stream
.FutureFrames
= 0;
1751 stream
.pInputSurface
= inputView
.get();
1756 destRect
.right
= mSwapChainSize
.width
;
1757 destRect
.bottom
= mSwapChainSize
.height
;
1759 videoContext
->VideoProcessorSetOutputTargetRect(videoProcessor
, TRUE
,
1761 videoContext
->VideoProcessorSetStreamDestRect(videoProcessor
, 0, TRUE
,
1764 sourceRect
.left
= 0;
1766 sourceRect
.right
= mVideoSize
.width
;
1767 sourceRect
.bottom
= mVideoSize
.height
;
1768 videoContext
->VideoProcessorSetStreamSourceRect(videoProcessor
, 0, TRUE
,
1772 RefPtr
<ID3D11Texture2D
> backBuf
;
1773 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1774 (void**)getter_AddRefs(backBuf
));
1776 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc
= {};
1777 outputDesc
.ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2D
;
1778 outputDesc
.Texture2D
.MipSlice
= 0;
1780 hr
= videoDevice
->CreateVideoProcessorOutputView(
1781 backBuf
, videoProcessorEnumerator
, &outputDesc
,
1782 getter_AddRefs(mOutputView
));
1784 gfxCriticalNote
<< "ID3D11VideoProcessorOutputView creation failed: "
1790 const UINT vendorId
= GetVendorId(videoDevice
);
1791 const auto powerIsCharging
= RenderThread::Get()->GetPowerIsCharging();
1792 if (gfx::gfxVars::WebRenderOverlayVpSuperResolution() &&
1793 !mVpSuperResolutionFailed
&& powerIsCharging
) {
1794 hr
= SetVpSuperResolution(vendorId
, videoContext
, videoProcessor
, true);
1796 if (hr
!= E_NOTIMPL
) {
1797 gfxCriticalNoteOnce
<< "SetVpSuperResolution failed: " << gfx::hexa(hr
);
1799 mVpSuperResolutionFailed
= true;
1803 if (mUseVpAutoHDR
) {
1804 hr
= SetVpAutoHDR(vendorId
, videoContext
, videoProcessor
, true);
1806 gfxCriticalNoteOnce
<< "SetVpAutoHDR failed: " << gfx::hexa(hr
);
1807 mVpAutoHDRFailed
= true;
1811 hr
= videoContext
->VideoProcessorBlt(videoProcessor
, mOutputView
, 0, 1,
1814 gfxCriticalNote
<< "VideoProcessorBlt failed: " << gfx::hexa(hr
);
1821 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1822 mOutputView
= nullptr;
1823 mVideoSwapChain
= nullptr;
1824 mDecodeSwapChain
= nullptr;
1825 mDecodeResource
= nullptr;
1826 if (mSwapChainSurfaceHandle
) {
1827 ::CloseHandle(mSwapChainSurfaceHandle
);
1828 mSwapChainSurfaceHandle
= 0;
1830 mUseVpAutoHDR
= false;
1833 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1834 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1837 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1838 RenderTextureHost
* texture
=
1839 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1840 RenderDcompSurfaceTextureHost
* renderTexture
=
1841 texture
? texture
->AsRenderDcompSurfaceTextureHost() : nullptr;
1842 if (!renderTexture
) {
1843 gfxCriticalNote
<< "Unsupported RenderTexture for DCSurfaceHandle: "
1844 << gfx::hexa(texture
);
1848 const auto handle
= renderTexture
->GetDcompSurfaceHandle();
1849 if (GetSurfaceHandle() == handle
) {
1853 LOG_H("AttachExternalImage, ext-image=%" PRIu64
", texture=%p, handle=%p",
1854 wr::AsUint64(aExternalImage
), renderTexture
, handle
);
1855 mDcompTextureHost
= renderTexture
;
1858 HANDLE
DCSurfaceHandle::GetSurfaceHandle() const {
1859 if (mDcompTextureHost
) {
1860 return mDcompTextureHost
->GetDcompSurfaceHandle();
1865 IDCompositionSurface
* DCSurfaceHandle::EnsureSurface() {
1866 if (auto* surface
= mDcompTextureHost
->GetSurface()) {
1870 // Texture host hasn't created the surface yet, ask it to create a new one.
1871 RefPtr
<IDCompositionDevice
> device
;
1872 HRESULT hr
= mDCLayerTree
->GetCompositionDevice()->QueryInterface(
1873 (IDCompositionDevice
**)getter_AddRefs(device
));
1876 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1881 return mDcompTextureHost
->CreateSurfaceFromDevice(device
);
1884 void DCSurfaceHandle::PresentSurfaceHandle() {
1885 LOG_H("PresentSurfaceHandle");
1886 if (IDCompositionSurface
* surface
= EnsureSurface()) {
1887 LOG_H("Set surface %p to visual", surface
);
1888 mVisual
->SetContent(surface
);
1890 mVisual
->SetContent(nullptr);
1894 DCTile::DCTile(DCLayerTree
* aDCLayerTree
) : mDCLayerTree(aDCLayerTree
) {}
1896 DCTile::~DCTile() {}
1898 bool DCTile::Initialize(int aX
, int aY
, wr::DeviceIntSize aSize
,
1899 bool aIsVirtualSurface
, bool aIsOpaque
,
1900 RefPtr
<IDCompositionVisual2
> mSurfaceVisual
) {
1901 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
1906 mIsOpaque
= aIsOpaque
;
1907 mIsVirtualSurface
= aIsVirtualSurface
;
1908 mNeedsFullDraw
= !aIsVirtualSurface
;
1910 if (aIsVirtualSurface
) {
1911 // Initially, the entire tile is considered valid, unless it is set by
1912 // the SetTileProperties method.
1915 mValidRect
.width
= aSize
.width
;
1916 mValidRect
.height
= aSize
.height
;
1919 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1920 // Create the visual and put it in the tree under the surface visual
1921 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1923 gfxCriticalNote
<< "Failed to CreateVisual for DCTile: " << gfx::hexa(hr
);
1926 mSurfaceVisual
->AddVisual(mVisual
, false, nullptr);
1927 // Position the tile relative to the surface visual
1928 mVisual
->SetOffsetX(aX
* aSize
.width
);
1929 mVisual
->SetOffsetY(aY
* aSize
.height
);
1930 // Clip the visual so it doesn't show anything until we update it
1931 D2D_RECT_F clip
= {0, 0, 0, 0};
1932 mVisual
->SetClip(clip
);
1933 // Create the underlying pixel buffer.
1934 mCompositionSurface
= CreateCompositionSurface(aSize
, aIsOpaque
);
1935 if (!mCompositionSurface
) {
1938 hr
= mVisual
->SetContent(mCompositionSurface
);
1940 gfxCriticalNote
<< "Failed to SetContent for DCTile: " << gfx::hexa(hr
);
1948 RefPtr
<IDCompositionSurface
> DCTile::CreateCompositionSurface(
1949 wr::DeviceIntSize aSize
, bool aIsOpaque
) {
1951 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1952 const auto alphaMode
=
1953 aIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1954 RefPtr
<IDCompositionSurface
> compositionSurface
;
1956 hr
= dCompDevice
->CreateSurface(aSize
.width
, aSize
.height
,
1957 DXGI_FORMAT_R8G8B8A8_UNORM
, alphaMode
,
1958 getter_AddRefs(compositionSurface
));
1960 gfxCriticalNote
<< "Failed to CreateSurface for DCTile: " << gfx::hexa(hr
);
1963 return compositionSurface
;
1966 RefPtr
<IDCompositionSurface
> DCTile::Bind(wr::DeviceIntRect aValidRect
) {
1967 if (mVisual
!= nullptr) {
1968 // Tile owns a visual, set the size of the visual to match the portion we
1969 // want to be visible.
1970 D2D_RECT_F clip_rect
;
1971 clip_rect
.left
= aValidRect
.min
.x
;
1972 clip_rect
.top
= aValidRect
.min
.y
;
1973 clip_rect
.right
= aValidRect
.max
.x
;
1974 clip_rect
.bottom
= aValidRect
.max
.y
;
1975 mVisual
->SetClip(clip_rect
);
1977 return mCompositionSurface
;
1980 GLuint
DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1981 wr::DeviceIntRect aDirtyRect
, wr::DeviceIntPoint
* aOffset
,
1982 RefPtr
<IDCompositionSurface
> aCompositionSurface
,
1983 wr::DeviceIntPoint aSurfaceOffset
) {
1984 MOZ_ASSERT(aCompositionSurface
.get());
1987 const auto gl
= GetGLContext();
1988 RefPtr
<ID3D11Texture2D
> backBuf
;
1992 update_rect
.left
= aSurfaceOffset
.x
+ aDirtyRect
.min
.x
;
1993 update_rect
.top
= aSurfaceOffset
.y
+ aDirtyRect
.min
.y
;
1994 update_rect
.right
= aSurfaceOffset
.x
+ aDirtyRect
.max
.x
;
1995 update_rect
.bottom
= aSurfaceOffset
.y
+ aDirtyRect
.max
.y
;
1996 hr
= aCompositionSurface
->BeginDraw(&update_rect
, __uuidof(ID3D11Texture2D
),
1997 (void**)getter_AddRefs(backBuf
), &offset
);
2000 LayoutDeviceIntRect rect
= widget::WinUtils::ToIntRect(update_rect
);
2002 gfxCriticalNote
<< "DCompositionSurface::BeginDraw failed: "
2003 << gfx::hexa(hr
) << " " << rect
;
2004 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW
);
2008 // DC includes the origin of the dirty / update rect in the draw offset,
2009 // undo that here since WR expects it to be an absolute offset.
2010 offset
.x
-= aDirtyRect
.min
.x
;
2011 offset
.y
-= aDirtyRect
.min
.y
;
2013 D3D11_TEXTURE2D_DESC desc
;
2014 backBuf
->GetDesc(&desc
);
2016 const auto& gle
= gl::GLContextEGL::Cast(gl
);
2017 const auto& egl
= gle
->mEgl
;
2019 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
2021 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
2022 const EGLint attribs
[] = {LOCAL_EGL_NONE
};
2023 mEGLImage
= egl
->fCreateImage(EGL_NO_CONTEXT
, LOCAL_EGL_D3D11_TEXTURE_ANGLE
,
2026 // Get the current FBO and RBO id, so we can restore them later
2027 GLint currentFboId
, currentRboId
;
2028 gl
->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
, ¤tFboId
);
2029 gl
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, ¤tRboId
);
2031 // Create a render buffer object that is backed by the EGL image.
2032 gl
->fGenRenderbuffers(1, &mColorRBO
);
2033 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, mColorRBO
);
2034 gl
->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, mEGLImage
);
2036 // Get or create an FBO for the specified dimensions
2037 GLuint fboId
= GetOrCreateFbo(desc
.Width
, desc
.Height
);
2039 // Attach the new renderbuffer to the FBO
2040 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
2041 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
2042 LOCAL_GL_COLOR_ATTACHMENT0
,
2043 LOCAL_GL_RENDERBUFFER
, mColorRBO
);
2045 // Restore previous FBO and RBO bindings
2046 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, currentFboId
);
2047 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, currentRboId
);
2049 aOffset
->x
= offset
.x
;
2050 aOffset
->y
= offset
.y
;
2055 void DCLayerTree::DestroyEGLSurface() {
2056 const auto gl
= GetGLContext();
2059 gl
->fDeleteRenderbuffers(1, &mColorRBO
);
2064 const auto& gle
= gl::GLContextEGL::Cast(gl
);
2065 const auto& egl
= gle
->mEgl
;
2066 egl
->fDestroyImage(mEGLImage
);
2067 mEGLImage
= EGL_NO_IMAGE
;
2073 color::ColorProfileDesc
DCLayerTree::QueryOutputColorProfile() {
2074 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
2075 // but we do need gfxPlatform::GetCMSOutputProfile().
2076 // So we steal what we need through the window:
2077 const auto outputProfileData
=
2078 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
2080 const auto qcmsProfile
= qcms_profile_from_memory(
2081 outputProfileData
.Elements(), outputProfileData
.Length());
2082 const auto release
= MakeScopeExit([&]() {
2084 qcms_profile_release(qcmsProfile
);
2088 const bool print
= gfxEnv::MOZ_GL_SPEW();
2090 const auto ret
= [&]() {
2092 return color::ColorProfileDesc::From(*qcmsProfile
);
2096 "Missing or failed to load display color profile, defaulting to "
2099 const auto MISSING_PROFILE_DEFAULT_SPACE
= color::ColorspaceDesc
{
2100 color::Chromaticities::Srgb(),
2101 color::PiecewiseGammaDesc::Srgb(),
2103 return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE
);
2107 const auto gammaGuess
= color::GuessGamma(ret
.linearFromTf
.r
);
2109 "Display profile:\n"
2110 " Approx Gamma: %f\n"
2111 " XYZ-D65 Red : %f, %f, %f\n"
2112 " XYZ-D65 Green: %f, %f, %f\n"
2113 " XYZ-D65 Blue : %f, %f, %f\n",
2114 gammaGuess
, ret
.xyzd65FromLinearRgb
.at(0, 0),
2115 ret
.xyzd65FromLinearRgb
.at(0, 1), ret
.xyzd65FromLinearRgb
.at(0, 2),
2117 ret
.xyzd65FromLinearRgb
.at(1, 0), ret
.xyzd65FromLinearRgb
.at(1, 1),
2118 ret
.xyzd65FromLinearRgb
.at(1, 2),
2120 ret
.xyzd65FromLinearRgb
.at(2, 0), ret
.xyzd65FromLinearRgb
.at(2, 1),
2121 ret
.xyzd65FromLinearRgb
.at(2, 2));
2127 inline D2D1_MATRIX_5X4_F
to_D2D1_MATRIX_5X4_F(const color::mat4
& m
) {
2128 return D2D1_MATRIX_5X4_F
{{{
2152 ColorManagementChain
ColorManagementChain::From(
2153 IDCompositionDevice3
& dcomp
,
2154 const color::ColorProfileConversionDesc
& conv
) {
2155 auto ret
= ColorManagementChain
{};
2157 #if !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
2159 const auto Append
= [&](const RefPtr
<IDCompositionFilterEffect
>& afterLast
) {
2161 afterLast
->SetInput(0, ret
.last
, 0);
2163 ret
.last
= afterLast
;
2166 const auto MaybeAppendColorMatrix
= [&](const color::mat4
& m
) {
2167 RefPtr
<IDCompositionColorMatrixEffect
> e
;
2168 if (approx(m
, color::mat4::Identity())) return e
;
2169 dcomp
.CreateColorMatrixEffect(getter_AddRefs(e
));
2172 e
->SetMatrix(to_D2D1_MATRIX_5X4_F(m
));
2176 const auto MaybeAppendTableTransfer
= [&](const color::RgbTransferTables
& t
) {
2177 RefPtr
<IDCompositionTableTransferEffect
> e
;
2178 if (!t
.r
.size() && !t
.g
.size() && !t
.b
.size()) return e
;
2179 dcomp
.CreateTableTransferEffect(getter_AddRefs(e
));
2182 e
->SetRedTable(t
.r
.data(), t
.r
.size());
2183 e
->SetGreenTable(t
.g
.data(), t
.g
.size());
2184 e
->SetBlueTable(t
.b
.data(), t
.b
.size());
2189 ret
.srcRgbFromSrcYuv
= MaybeAppendColorMatrix(conv
.srcRgbFromSrcYuv
);
2190 ret
.srcLinearFromSrcTf
= MaybeAppendTableTransfer(conv
.srcLinearFromSrcTf
);
2191 ret
.dstLinearFromSrcLinear
=
2192 MaybeAppendColorMatrix(color::mat4(conv
.dstLinearFromSrcLinear
));
2193 ret
.dstTfFromDstLinear
= MaybeAppendTableTransfer(conv
.dstTfFromDstLinear
);
2195 #endif // !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
2200 ColorManagementChain::~ColorManagementChain() = default;
2203 } // namespace mozilla