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
);
73 auto layerTree
= MakeUnique
<DCLayerTree
>(aGL
, aEGLConfig
, aDevice
, aCtx
,
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 HWND aHwnd
, IDCompositionDevice2
* aCompositionDevice
)
88 mEGLConfig(aEGLConfig
),
92 mCompositionDevice(aCompositionDevice
),
94 mDebugVisualRedrawRegions(false),
95 mEGLImage(EGL_NO_IMAGE
),
97 mPendingCommit(false) {
98 LOG("DCLayerTree::DCLayerTree()");
101 DCLayerTree::~DCLayerTree() {
102 LOG("DCLayerTree::~DCLayerTree()");
104 ReleaseNativeCompositorResources();
107 void DCLayerTree::ReleaseNativeCompositorResources() {
108 const auto gl
= GetGLContext();
112 // Delete any cached FBO objects
113 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
114 gl
->fDeleteRenderbuffers(1, &it
->depthRboId
);
115 gl
->fDeleteFramebuffers(1, &it
->fboId
);
119 bool DCLayerTree::Initialize(HWND aHwnd
, nsACString
& aError
) {
122 RefPtr
<IDCompositionDesktopDevice
> desktopDevice
;
123 hr
= mCompositionDevice
->QueryInterface(
124 (IDCompositionDesktopDevice
**)getter_AddRefs(desktopDevice
));
126 aError
.Assign(nsPrintfCString(
127 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr
));
131 hr
= desktopDevice
->CreateTargetForHwnd(aHwnd
, TRUE
,
132 getter_AddRefs(mCompositionTarget
));
134 aError
.Assign(nsPrintfCString(
135 "DCLayerTree(create DCompositionTarget failed %lx)", hr
));
139 hr
= mCompositionDevice
->CreateVisual(getter_AddRefs(mRootVisual
));
141 aError
.Assign(nsPrintfCString(
142 "DCLayerTree(create root DCompositionVisual failed %lx)", hr
));
147 mCompositionDevice
->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual
));
149 aError
.Assign(nsPrintfCString(
150 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr
));
154 if (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin() ||
155 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) {
156 if (!InitializeVideoOverlaySupport()) {
157 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY
);
160 if (!sGpuOverlayInfo
) {
161 // Set default if sGpuOverlayInfo was not set.
162 sGpuOverlayInfo
= new GpuOverlayInfo();
165 // Initialize SwapChainInfo
166 SupportsSwapChainTearing();
168 mCompositionTarget
->SetRoot(mRootVisual
);
169 // Set interporation mode to nearest, to ensure 1:1 sampling.
170 // By default, a visual inherits the interpolation mode of the parent visual.
171 // If no visuals set the interpolation mode, the default for the entire visual
172 // tree is nearest neighbor interpolation.
173 mRootVisual
->SetBitmapInterpolationMode(
174 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
178 bool FlagsSupportsOverlays(UINT flags
) {
179 return (flags
& (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
|
180 DXGI_OVERLAY_SUPPORT_FLAG_SCALING
));
183 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
184 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat
,
185 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace
,
186 RefPtr
<IDXGIOutput
> aOutput
,
187 RefPtr
<ID3D11Device
> aD3d11Device
) {
188 UINT colorSpaceSupportFlags
= 0;
189 RefPtr
<IDXGIOutput4
> output4
;
191 if (FAILED(aOutput
->QueryInterface(__uuidof(IDXGIOutput4
),
192 getter_AddRefs(output4
)))) {
196 if (FAILED(output4
->CheckOverlayColorSpaceSupport(
197 aDxgiFormat
, aDxgiColorSpace
, aD3d11Device
,
198 &colorSpaceSupportFlags
))) {
202 return (colorSpaceSupportFlags
&
203 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT
);
206 bool DCLayerTree::InitializeVideoOverlaySupport() {
207 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
211 hr
= mDevice
->QueryInterface(
212 (ID3D11VideoDevice
**)getter_AddRefs(mVideoDevice
));
214 gfxCriticalNote
<< "Failed to get D3D11VideoDevice: " << gfx::hexa(hr
);
219 mCtx
->QueryInterface((ID3D11VideoContext
**)getter_AddRefs(mVideoContext
));
221 gfxCriticalNote
<< "Failed to get D3D11VideoContext: " << gfx::hexa(hr
);
225 if (sGpuOverlayInfo
) {
229 UniquePtr
<GpuOverlayInfo
> info
= MakeUnique
<GpuOverlayInfo
>();
231 RefPtr
<IDXGIDevice
> dxgiDevice
;
232 RefPtr
<IDXGIAdapter
> adapter
;
233 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
234 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
238 RefPtr
<IDXGIOutput
> output
;
239 if (FAILED(adapter
->EnumOutputs(i
++, getter_AddRefs(output
)))) {
242 RefPtr
<IDXGIOutput3
> output3
;
243 if (FAILED(output
->QueryInterface(__uuidof(IDXGIOutput3
),
244 getter_AddRefs(output3
)))) {
248 output3
->CheckOverlaySupport(DXGI_FORMAT_NV12
, mDevice
,
249 &info
->mNv12OverlaySupportFlags
);
250 output3
->CheckOverlaySupport(DXGI_FORMAT_YUY2
, mDevice
,
251 &info
->mYuy2OverlaySupportFlags
);
252 output3
->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM
, mDevice
,
253 &info
->mBgra8OverlaySupportFlags
);
254 output3
->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM
, mDevice
,
255 &info
->mRgb10a2OverlaySupportFlags
);
257 if (FlagsSupportsOverlays(info
->mNv12OverlaySupportFlags
)) {
258 // NV12 format is preferred if it's supported.
259 info
->mOverlayFormatUsed
= DXGI_FORMAT_NV12
;
260 info
->mSupportsHardwareOverlays
= true;
263 if (!info
->mSupportsHardwareOverlays
&&
264 FlagsSupportsOverlays(info
->mYuy2OverlaySupportFlags
)) {
265 // If NV12 isn't supported, fallback to YUY2 if it's supported.
266 info
->mOverlayFormatUsed
= DXGI_FORMAT_YUY2
;
267 info
->mSupportsHardwareOverlays
= true;
270 // RGB10A2 overlay is used for displaying HDR content. In Intel's
271 // platform, RGB10A2 overlay is enabled only when
272 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
273 if (FlagsSupportsOverlays(info
->mRgb10a2OverlaySupportFlags
)) {
274 if (!CheckOverlayColorSpaceSupport(
275 DXGI_FORMAT_R10G10B10A2_UNORM
,
276 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
, output
, mDevice
))
277 info
->mRgb10a2OverlaySupportFlags
= 0;
280 // Early out after the first output that reports overlay support. All
281 // outputs are expected to report the same overlay support according to
282 // Microsoft's WDDM documentation:
283 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
284 if (info
->mSupportsHardwareOverlays
) {
289 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
290 info
->mOverlayFormatUsed
= DXGI_FORMAT_B8G8R8A8_UNORM
;
291 info
->mSupportsHardwareOverlays
= false;
294 info
->mSupportsOverlays
= info
->mSupportsHardwareOverlays
;
296 // Note: "UniquePtr::release" here is saying "release your ownership stake
297 // on your pointer, so that our StaticAutoPtr can take over ownership".
298 // (StaticAutoPtr doesn't have a move constructor that could directly steal
299 // the contents of a UniquePtr via std::move().)
300 sGpuOverlayInfo
= info
.release();
302 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
303 gpuParent
->NotifyOverlayInfo(GetOverlayInfo());
309 DCSurface
* DCLayerTree::GetSurface(wr::NativeSurfaceId aId
) const {
310 auto surface_it
= mDCSurfaces
.find(aId
);
311 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
312 return surface_it
->second
.get();
315 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1
* aSwapChain
) {
316 LOG("DCLayerTree::SetDefaultSwapChain()");
318 mRootVisual
->AddVisual(mDefaultSwapChainVisual
, TRUE
, nullptr);
319 mDefaultSwapChainVisual
->SetContent(aSwapChain
);
320 // Default SwapChain's visual does not need linear interporation.
321 mDefaultSwapChainVisual
->SetBitmapInterpolationMode(
322 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
323 mPendingCommit
= true;
326 void DCLayerTree::MaybeUpdateDebug() {
327 bool updated
= false;
328 updated
|= MaybeUpdateDebugCounter();
329 updated
|= MaybeUpdateDebugVisualRedrawRegions();
331 mPendingCommit
= true;
335 void DCLayerTree::MaybeCommit() {
336 if (!mPendingCommit
) {
339 mCompositionDevice
->Commit();
342 void DCLayerTree::WaitForCommitCompletion() {
343 mCompositionDevice
->WaitForCommitCompletion();
346 void DCLayerTree::DisableNativeCompositor() {
347 MOZ_ASSERT(mCurrentSurface
.isNothing());
348 MOZ_ASSERT(mCurrentLayers
.empty());
350 ReleaseNativeCompositorResources();
352 mRootVisual
->RemoveAllVisuals();
355 bool DCLayerTree::MaybeUpdateDebugCounter() {
356 bool debugCounter
= StaticPrefs::gfx_webrender_debug_dcomp_counter();
357 if (mDebugCounter
== debugCounter
) {
361 RefPtr
<IDCompositionDeviceDebug
> debugDevice
;
362 HRESULT hr
= mCompositionDevice
->QueryInterface(
363 (IDCompositionDeviceDebug
**)getter_AddRefs(debugDevice
));
369 debugDevice
->EnableDebugCounters();
371 debugDevice
->DisableDebugCounters();
374 mDebugCounter
= debugCounter
;
378 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
379 bool debugVisualRedrawRegions
=
380 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
381 if (mDebugVisualRedrawRegions
== debugVisualRedrawRegions
) {
385 RefPtr
<IDCompositionVisualDebug
> visualDebug
;
386 HRESULT hr
= mRootVisual
->QueryInterface(
387 (IDCompositionVisualDebug
**)getter_AddRefs(visualDebug
));
392 if (debugVisualRedrawRegions
) {
393 visualDebug
->EnableRedrawRegions();
395 visualDebug
->DisableRedrawRegions();
398 mDebugVisualRedrawRegions
= debugVisualRedrawRegions
;
402 void DCLayerTree::CompositorBeginFrame() {
404 mUsedOverlayTypesInFrame
= DCompOverlayTypes::NO_OVERLAY
;
407 void DCLayerTree::CompositorEndFrame() {
408 auto start
= TimeStamp::Now();
409 // Check if the visual tree of surfaces is the same as last frame.
410 bool same
= mPrevLayers
== mCurrentLayers
;
413 // If not, we need to rebuild the visual tree. Note that addition or
414 // removal of tiles no longer needs to rebuild the main visual tree
415 // here, since they are added as children of the surface visual.
416 mRootVisual
->RemoveAllVisuals();
419 for (auto it
= mCurrentLayers
.begin(); it
!= mCurrentLayers
.end(); ++it
) {
420 auto surface_it
= mDCSurfaces
.find(*it
);
421 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
422 const auto surface
= surface_it
->second
.get();
423 // Ensure surface is trimmed to updated tile valid rects
424 surface
->UpdateAllocatedRect();
426 // Add surfaces in z-order they were added to the scene.
427 const auto visual
= surface
->GetVisual();
428 mRootVisual
->AddVisual(visual
, false, nullptr);
432 mPrevLayers
.swap(mCurrentLayers
);
433 mCurrentLayers
.clear();
435 mCompositionDevice
->Commit();
437 auto end
= TimeStamp::Now();
438 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
439 (end
- start
).ToMilliseconds() * 10.);
441 // Remove any framebuffers that haven't been
442 // used in the last 60 frames.
444 // This should use nsTArray::RemoveElementsBy once
445 // CachedFrameBuffer is able to properly destroy
446 // itself in the destructor.
447 const auto gl
= GetGLContext();
448 for (uint32_t i
= 0, len
= mFrameBuffers
.Length(); i
< len
; ++i
) {
449 auto& fb
= mFrameBuffers
[i
];
450 if ((mCurrentFrame
- fb
.lastFrameUsed
) > 60) {
451 gl
->fDeleteRenderbuffers(1, &fb
.depthRboId
);
452 gl
->fDeleteFramebuffers(1, &fb
.fboId
);
453 mFrameBuffers
.UnorderedRemoveElementAt(i
);
454 --i
; // Examine the element again, if necessary.
459 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
463 // Disable video overlay if mCompositionDevice->Commit() with video overlay is
464 // too slow. It drops fps.
466 const auto maxCommitWaitDurationMs
= 20;
467 const auto maxSlowCommitCount
= 5;
468 const auto commitDurationMs
=
469 static_cast<uint32_t>((end
- start
).ToMilliseconds());
471 nsPrintfCString
marker("CommitWait overlay %u %ums ",
472 (uint8_t)mUsedOverlayTypesInFrame
, commitDurationMs
);
473 PROFILER_MARKER_TEXT("CommitWait", GRAPHICS
, {}, marker
);
475 if (mUsedOverlayTypesInFrame
!= DCompOverlayTypes::NO_OVERLAY
&&
476 commitDurationMs
> maxCommitWaitDurationMs
) {
479 mSlowCommitCount
= 0;
482 if (mSlowCommitCount
<= maxSlowCommitCount
) {
486 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
487 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
488 RenderThread::Get()->NotifyWebRenderError(
489 wr::WebRenderError::VIDEO_SW_OVERLAY
);
491 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::HARDWARE_DECODED_VIDEO
) {
492 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
493 RenderThread::Get()->NotifyWebRenderError(
494 wr::WebRenderError::VIDEO_HW_OVERLAY
);
498 void DCLayerTree::Bind(wr::NativeTileId aId
, wr::DeviceIntPoint
* aOffset
,
499 uint32_t* aFboId
, wr::DeviceIntRect aDirtyRect
,
500 wr::DeviceIntRect aValidRect
) {
501 auto surface
= GetSurface(aId
.surface_id
);
502 auto tile
= surface
->GetTile(aId
.x
, aId
.y
);
503 wr::DeviceIntPoint targetOffset
{0, 0};
505 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
506 // IDCompositionVirtualSurface owned by the DCSurface.
507 RefPtr
<IDCompositionSurface
> compositionSurface
;
508 if (surface
->mIsVirtualSurface
) {
509 gfx::IntRect
validRect(aValidRect
.min
.x
, aValidRect
.min
.y
,
510 aValidRect
.width(), aValidRect
.height());
511 if (!tile
->mValidRect
.IsEqualEdges(validRect
)) {
512 tile
->mValidRect
= validRect
;
513 surface
->DirtyAllocatedRect();
515 wr::DeviceIntSize tileSize
= surface
->GetTileSize();
516 compositionSurface
= surface
->GetCompositionSurface();
517 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
518 targetOffset
.x
= virtualOffset
.x
+ tileSize
.width
* aId
.x
;
519 targetOffset
.y
= virtualOffset
.y
+ tileSize
.height
* aId
.y
;
521 compositionSurface
= tile
->Bind(aValidRect
);
524 if (tile
->mNeedsFullDraw
) {
525 // dcomp requires that the first BeginDraw on a non-virtual surface is the
526 // full size of the pixel buffer.
527 auto tileSize
= surface
->GetTileSize();
528 aDirtyRect
.min
.x
= 0;
529 aDirtyRect
.min
.y
= 0;
530 aDirtyRect
.max
.x
= tileSize
.width
;
531 aDirtyRect
.max
.y
= tileSize
.height
;
532 tile
->mNeedsFullDraw
= false;
535 *aFboId
= CreateEGLSurfaceForCompositionSurface(
536 aDirtyRect
, aOffset
, compositionSurface
, targetOffset
);
537 mCurrentSurface
= Some(compositionSurface
);
540 void DCLayerTree::Unbind() {
541 if (mCurrentSurface
.isNothing()) {
545 RefPtr
<IDCompositionSurface
> surface
= mCurrentSurface
.ref();
549 mCurrentSurface
= Nothing();
552 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId
,
553 wr::DeviceIntPoint aVirtualOffset
,
554 wr::DeviceIntSize aTileSize
, bool aIsOpaque
) {
555 auto it
= mDCSurfaces
.find(aId
);
556 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
557 if (it
!= mDCSurfaces
.end()) {
558 // DCSurface already exists.
562 // Tile size needs to be positive.
563 if (aTileSize
.width
<= 0 || aTileSize
.height
<= 0) {
564 gfxCriticalNote
<< "TileSize is not positive aId: " << wr::AsUint64(aId
)
565 << " aTileSize(" << aTileSize
.width
<< ","
566 << aTileSize
.height
<< ")";
569 bool isVirtualSurface
=
570 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
571 auto surface
= MakeUnique
<DCSurface
>(aTileSize
, aVirtualOffset
,
572 isVirtualSurface
, aIsOpaque
, this);
573 if (!surface
->Initialize()) {
574 gfxCriticalNote
<< "Failed to initialize DCSurface: " << wr::AsUint64(aId
);
578 mDCSurfaces
[aId
] = std::move(surface
);
581 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId
,
583 auto it
= mDCSurfaces
.find(aId
);
584 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
586 auto surface
= MakeUnique
<DCExternalSurfaceWrapper
>(aIsOpaque
, this);
587 if (!surface
->Initialize()) {
588 gfxCriticalNote
<< "Failed to initialize DCExternalSurfaceWrapper: "
589 << wr::AsUint64(aId
);
593 mDCSurfaces
[aId
] = std::move(surface
);
596 void DCLayerTree::DestroySurface(NativeSurfaceId aId
) {
597 auto surface_it
= mDCSurfaces
.find(aId
);
598 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
599 auto surface
= surface_it
->second
.get();
601 mRootVisual
->RemoveVisual(surface
->GetVisual());
602 mDCSurfaces
.erase(surface_it
);
605 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
606 auto surface
= GetSurface(aId
);
607 surface
->CreateTile(aX
, aY
);
610 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
611 auto surface
= GetSurface(aId
);
612 surface
->DestroyTile(aX
, aY
);
615 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId
,
616 wr::ExternalImageId aExternalImage
) {
617 auto surface_it
= mDCSurfaces
.find(aId
);
618 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
619 surface_it
->second
->AttachExternalImage(aExternalImage
);
622 void DCExternalSurfaceWrapper::AttachExternalImage(
623 wr::ExternalImageId aExternalImage
) {
624 if (auto* surface
= EnsureSurfaceForExternalImage(aExternalImage
)) {
625 surface
->AttachExternalImage(aExternalImage
);
631 template <class FromT
>
632 [[nodiscard
]] static inline RefPtr
<ToT
> From(FromT
* const from
) {
634 (void)from
->QueryInterface(static_cast<ToT
**>(getter_AddRefs(to
)));
639 DCSurface
* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
640 wr::ExternalImageId aExternalImage
) {
642 return mSurface
.get();
645 // Create a new surface based on the texture type.
646 RenderTextureHost
* texture
=
647 RenderThread::Get()->GetRenderTexture(aExternalImage
);
648 if (texture
&& texture
->AsRenderDXGITextureHost()) {
649 mSurface
.reset(new DCSurfaceVideo(mIsOpaque
, mDCLayerTree
));
650 if (!mSurface
->Initialize()) {
651 gfxCriticalNote
<< "Failed to initialize DCSurfaceVideo: "
652 << wr::AsUint64(aExternalImage
);
655 } else if (texture
&& texture
->AsRenderDcompSurfaceTextureHost()) {
656 mSurface
.reset(new DCSurfaceHandle(mIsOpaque
, mDCLayerTree
));
657 if (!mSurface
->Initialize()) {
658 gfxCriticalNote
<< "Failed to initialize DCSurfaceHandle: "
659 << wr::AsUint64(aExternalImage
);
664 gfxCriticalNote
<< "Failed to create a surface for external image: "
665 << gfx::hexa(texture
);
669 // Add surface's visual which will contain video data to our root visual.
670 const auto surfaceVisual
= mSurface
->GetVisual();
671 mVisual
->AddVisual(surfaceVisual
, true, nullptr);
674 // Apply color management.
677 const auto cmsMode
= GfxColorManagementMode();
678 if (cmsMode
== CMSMode::Off
) return;
680 const auto dcomp
= mDCLayerTree
->GetCompositionDevice();
681 const auto dcomp3
= QI
<IDCompositionDevice3
>::From(dcomp
);
684 "No IDCompositionDevice3, cannot use dcomp for color management.");
690 const auto cspace
= [&]() {
691 const auto rangedCspace
= texture
->GetYUVColorSpace();
692 const auto info
= FromYUVRangedColorSpace(rangedCspace
);
693 auto ret
= ToColorSpace2(info
.space
);
694 if (ret
== gfx::ColorSpace2::Display
&& cmsMode
== CMSMode::All
) {
695 ret
= gfx::ColorSpace2::SRGB
;
700 const bool rec709GammaAsSrgb
=
701 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
702 const bool rec2020GammaAsRec709
=
703 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
705 auto cspaceDesc
= color::ColorspaceDesc
{};
707 case gfx::ColorSpace2::Display
:
708 return; // No color management needed!
709 case gfx::ColorSpace2::SRGB
:
710 cspaceDesc
.chrom
= color::Chromaticities::Srgb();
711 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
714 case gfx::ColorSpace2::DISPLAY_P3
:
715 cspaceDesc
.chrom
= color::Chromaticities::DisplayP3();
716 cspaceDesc
.tf
= color::PiecewiseGammaDesc::DisplayP3();
719 case gfx::ColorSpace2::BT601_525
:
720 cspaceDesc
.chrom
= color::Chromaticities::Rec601_525_Ntsc();
721 if (rec709GammaAsSrgb
) {
722 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
724 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
728 case gfx::ColorSpace2::BT709
:
729 cspaceDesc
.chrom
= color::Chromaticities::Rec709();
730 if (rec709GammaAsSrgb
) {
731 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
733 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
737 case gfx::ColorSpace2::BT2020
:
738 cspaceDesc
.chrom
= color::Chromaticities::Rec2020();
739 if (rec2020GammaAsRec709
&& rec709GammaAsSrgb
) {
740 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
741 } else if (rec2020GammaAsRec709
) {
742 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
744 // Just Rec709 with slightly more precision.
745 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec2020_12bit();
750 const auto cprofileIn
= color::ColorProfileDesc::From(cspaceDesc
);
751 auto cprofileOut
= mDCLayerTree
->OutputColorProfile();
752 bool pretendSrgb
= true;
754 cprofileOut
= color::ColorProfileDesc::From({
755 color::Chromaticities::Srgb(),
756 color::PiecewiseGammaDesc::Srgb(),
759 const auto conversion
= color::ColorProfileConversionDesc::From({
766 auto chain
= ColorManagementChain::From(*dcomp3
, conversion
);
767 mCManageChain
= Some(chain
);
769 surfaceVisual
->SetEffect(mCManageChain
->last
.get());
772 return mSurface
.get();
775 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix
& aTransform
) {
776 MOZ_ASSERT(mSurface
);
777 if (auto* surface
= mSurface
->AsDCSurfaceVideo()) {
778 if (surface
->CalculateSwapChainSize(aTransform
)) {
779 surface
->PresentVideo();
781 } else if (auto* surface
= mSurface
->AsDCSurfaceHandle()) {
782 surface
->PresentSurfaceHandle();
786 template <typename T
>
787 static inline D2D1_RECT_F
D2DRect(const T
& aRect
) {
788 return D2D1::RectF(aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost());
791 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const gfx::Matrix
& aTransform
) {
792 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
, aTransform
._21
,
793 aTransform
._22
, aTransform
._31
, aTransform
._32
);
796 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId
,
797 const wr::CompositorSurfaceTransform
& aTransform
,
798 wr::DeviceIntRect aClipRect
,
799 wr::ImageRendering aImageRendering
) {
800 auto it
= mDCSurfaces
.find(aId
);
801 MOZ_RELEASE_ASSERT(it
!= mDCSurfaces
.end());
802 const auto surface
= it
->second
.get();
803 const auto visual
= surface
->GetVisual();
805 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
807 float sx
= aTransform
.scale
.x
;
808 float sy
= aTransform
.scale
.y
;
809 float tx
= aTransform
.offset
.x
;
810 float ty
= aTransform
.offset
.y
;
811 gfx::Matrix
transform(sx
, 0.0, 0.0, sy
, tx
, ty
);
813 surface
->PresentExternalSurface(transform
);
815 transform
.PreTranslate(-virtualOffset
.x
, -virtualOffset
.y
);
817 // The DirectComposition API applies clipping *before* any
818 // transforms/offset, whereas we want the clip applied after. Right now, we
819 // only support rectilinear transforms, and then we transform our clip into
820 // pre-transform coordinate space for it to be applied there.
821 // DirectComposition does have an option for pre-transform clipping, if you
822 // create an explicit IDCompositionEffectGroup object and set a 3D transform
823 // on that. I suspect that will perform worse though, so we should only do
824 // that for complex transforms (which are never provided right now).
825 MOZ_ASSERT(transform
.IsRectilinear());
826 gfx::Rect clip
= transform
.Inverse().TransformBounds(gfx::Rect(
827 aClipRect
.min
.x
, aClipRect
.min
.y
, aClipRect
.width(), aClipRect
.height()));
828 // Set the clip rect - converting from world space to the pre-offset space
829 // that DC requires for rectangle clips.
830 visual
->SetClip(D2DRect(clip
));
832 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
833 // the D3D API level (unless we QI to IDCompositionVisual3, which might
834 // not be available?).
835 // Should we assert here, or restrict at the WR API level.
836 visual
->SetTransform(D2DMatrix(transform
));
838 if (aImageRendering
== wr::ImageRendering::Auto
) {
839 visual
->SetBitmapInterpolationMode(
840 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR
);
842 visual
->SetBitmapInterpolationMode(
843 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
846 mCurrentLayers
.push_back(aId
);
849 GLuint
DCLayerTree::GetOrCreateFbo(int aWidth
, int aHeight
) {
850 const auto gl
= GetGLContext();
853 // Check if we have a cached FBO with matching dimensions
854 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
855 if (it
->width
== aWidth
&& it
->height
== aHeight
) {
857 it
->lastFrameUsed
= mCurrentFrame
;
862 // If not, create a new FBO with attached depth buffer
864 // Create the depth buffer
866 gl
->fGenRenderbuffers(1, &depthRboId
);
867 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, depthRboId
);
868 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, LOCAL_GL_DEPTH_COMPONENT24
,
871 // Create the framebuffer and attach the depth buffer to it
872 gl
->fGenFramebuffers(1, &fboId
);
873 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
874 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
875 LOCAL_GL_DEPTH_ATTACHMENT
,
876 LOCAL_GL_RENDERBUFFER
, depthRboId
);
878 // Store this in the cache for future calls.
879 // TODO(gw): Maybe we should periodically scan this list and remove old
881 // haven't been used for some time?
882 DCLayerTree::CachedFrameBuffer frame_buffer_info
;
883 frame_buffer_info
.width
= aWidth
;
884 frame_buffer_info
.height
= aHeight
;
885 frame_buffer_info
.fboId
= fboId
;
886 frame_buffer_info
.depthRboId
= depthRboId
;
887 frame_buffer_info
.lastFrameUsed
= mCurrentFrame
;
888 mFrameBuffers
.AppendElement(frame_buffer_info
);
894 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize
& aInputSize
,
895 const gfx::IntSize
& aOutputSize
) {
898 if (!mVideoDevice
|| !mVideoContext
) {
902 if (mVideoProcessor
&& (aInputSize
<= mVideoInputSize
) &&
903 (aOutputSize
<= mVideoOutputSize
)) {
907 mVideoProcessor
= nullptr;
908 mVideoProcessorEnumerator
= nullptr;
910 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc
= {};
911 desc
.InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE
;
912 desc
.InputFrameRate
.Numerator
= 60;
913 desc
.InputFrameRate
.Denominator
= 1;
914 desc
.InputWidth
= aInputSize
.width
;
915 desc
.InputHeight
= aInputSize
.height
;
916 desc
.OutputFrameRate
.Numerator
= 60;
917 desc
.OutputFrameRate
.Denominator
= 1;
918 desc
.OutputWidth
= aOutputSize
.width
;
919 desc
.OutputHeight
= aOutputSize
.height
;
920 desc
.Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
;
922 hr
= mVideoDevice
->CreateVideoProcessorEnumerator(
923 &desc
, getter_AddRefs(mVideoProcessorEnumerator
));
925 gfxCriticalNote
<< "Failed to create VideoProcessorEnumerator: "
930 hr
= mVideoDevice
->CreateVideoProcessor(mVideoProcessorEnumerator
, 0,
931 getter_AddRefs(mVideoProcessor
));
933 mVideoProcessor
= nullptr;
934 mVideoProcessorEnumerator
= nullptr;
935 gfxCriticalNote
<< "Failed to create VideoProcessor: " << gfx::hexa(hr
);
939 // Reduce power cosumption
940 // By default, the driver might perform certain processing tasks
942 mVideoContext
->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor
, 0,
945 mVideoInputSize
= aInputSize
;
946 mVideoOutputSize
= aOutputSize
;
951 bool DCLayerTree::SupportsHardwareOverlays() {
952 return sGpuOverlayInfo
->mSupportsHardwareOverlays
;
955 bool DCLayerTree::SupportsSwapChainTearing() {
956 RefPtr
<ID3D11Device
> device
= mDevice
;
957 static const bool supported
= [device
] {
958 RefPtr
<IDXGIDevice
> dxgiDevice
;
959 RefPtr
<IDXGIAdapter
> adapter
;
960 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
961 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
963 RefPtr
<IDXGIFactory5
> dxgiFactory
;
964 HRESULT hr
= adapter
->GetParent(
965 IID_PPV_ARGS((IDXGIFactory5
**)getter_AddRefs(dxgiFactory
)));
970 BOOL presentAllowTearing
= FALSE
;
971 hr
= dxgiFactory
->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING
,
972 &presentAllowTearing
,
973 sizeof(presentAllowTearing
));
978 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
979 gpuParent
->NotifySwapChainInfo(
980 layers::SwapChainInfo(!!presentAllowTearing
));
981 } else if (XRE_IsParentProcess()) {
982 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
984 return !!presentAllowTearing
;
989 DXGI_FORMAT
DCLayerTree::GetOverlayFormatForSDR() {
990 return sGpuOverlayInfo
->mOverlayFormatUsed
;
993 static layers::OverlaySupportType
FlagsToOverlaySupportType(
994 UINT aFlags
, bool aSoftwareOverlaySupported
) {
995 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_SCALING
) {
996 return layers::OverlaySupportType::Scaling
;
998 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
) {
999 return layers::OverlaySupportType::Direct
;
1001 if (aSoftwareOverlaySupported
) {
1002 return layers::OverlaySupportType::Software
;
1004 return layers::OverlaySupportType::None
;
1007 layers::OverlayInfo
DCLayerTree::GetOverlayInfo() {
1008 layers::OverlayInfo info
;
1010 info
.mSupportsOverlays
= sGpuOverlayInfo
->mSupportsHardwareOverlays
;
1012 FlagsToOverlaySupportType(sGpuOverlayInfo
->mNv12OverlaySupportFlags
,
1013 /* aSoftwareOverlaySupported */ false);
1015 FlagsToOverlaySupportType(sGpuOverlayInfo
->mYuy2OverlaySupportFlags
,
1016 /* aSoftwareOverlaySupported */ false);
1017 info
.mBgra8Overlay
=
1018 FlagsToOverlaySupportType(sGpuOverlayInfo
->mBgra8OverlaySupportFlags
,
1019 /* aSoftwareOverlaySupported */ true);
1020 info
.mRgb10a2Overlay
=
1021 FlagsToOverlaySupportType(sGpuOverlayInfo
->mRgb10a2OverlaySupportFlags
,
1022 /* aSoftwareOverlaySupported */ false);
1027 void DCLayerTree::SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes
) {
1028 mUsedOverlayTypesInFrame
|= aTypes
;
1031 DCSurface::DCSurface(wr::DeviceIntSize aTileSize
,
1032 wr::DeviceIntPoint aVirtualOffset
, bool aIsVirtualSurface
,
1033 bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1034 : mIsVirtualSurface(aIsVirtualSurface
),
1035 mDCLayerTree(aDCLayerTree
),
1036 mTileSize(aTileSize
),
1037 mIsOpaque(aIsOpaque
),
1038 mAllocatedRectDirty(true),
1039 mVirtualOffset(aVirtualOffset
) {}
1041 DCSurface::~DCSurface() {}
1043 bool DCSurface::Initialize() {
1044 // Create a visual for tiles to attach to, whether virtual or not.
1046 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1047 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1049 gfxCriticalNote
<< "Failed to create DCompositionVisual: " << gfx::hexa(hr
);
1053 // If virtual surface is enabled, create and attach to visual, in this case
1054 // the tiles won't own visuals or surfaces.
1055 if (mIsVirtualSurface
) {
1056 DXGI_ALPHA_MODE alpha_mode
=
1057 mIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1059 hr
= dCompDevice
->CreateVirtualSurface(
1060 VIRTUAL_SURFACE_SIZE
, VIRTUAL_SURFACE_SIZE
, DXGI_FORMAT_R8G8B8A8_UNORM
,
1061 alpha_mode
, getter_AddRefs(mVirtualSurface
));
1062 MOZ_ASSERT(SUCCEEDED(hr
));
1064 // Bind the surface memory to this visual
1065 hr
= mVisual
->SetContent(mVirtualSurface
);
1066 MOZ_ASSERT(SUCCEEDED(hr
));
1072 void DCSurface::CreateTile(int32_t aX
, int32_t aY
) {
1073 TileKey
key(aX
, aY
);
1074 MOZ_RELEASE_ASSERT(mDCTiles
.find(key
) == mDCTiles
.end());
1076 auto tile
= MakeUnique
<DCTile
>(mDCLayerTree
);
1077 if (!tile
->Initialize(aX
, aY
, mTileSize
, mIsVirtualSurface
, mIsOpaque
,
1079 gfxCriticalNote
<< "Failed to initialize DCTile: " << aX
<< aY
;
1083 if (mIsVirtualSurface
) {
1084 mAllocatedRectDirty
= true;
1086 mVisual
->AddVisual(tile
->GetVisual(), false, nullptr);
1089 mDCTiles
[key
] = std::move(tile
);
1092 void DCSurface::DestroyTile(int32_t aX
, int32_t aY
) {
1093 TileKey
key(aX
, aY
);
1094 if (mIsVirtualSurface
) {
1095 mAllocatedRectDirty
= true;
1097 auto tile
= GetTile(aX
, aY
);
1098 mVisual
->RemoveVisual(tile
->GetVisual());
1100 mDCTiles
.erase(key
);
1103 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty
= true; }
1105 void DCSurface::UpdateAllocatedRect() {
1106 if (mAllocatedRectDirty
) {
1107 if (mVirtualSurface
) {
1108 // The virtual surface may have holes in it (for example, an empty tile
1109 // that has no primitives). Instead of trimming to a single bounding
1110 // rect, supply the rect of each valid tile to handle this case.
1111 std::vector
<RECT
> validRects
;
1113 for (auto it
= mDCTiles
.begin(); it
!= mDCTiles
.end(); ++it
) {
1114 auto tile
= GetTile(it
->first
.mX
, it
->first
.mY
);
1117 rect
.left
= (LONG
)(mVirtualOffset
.x
+ it
->first
.mX
* mTileSize
.width
+
1118 tile
->mValidRect
.x
);
1119 rect
.top
= (LONG
)(mVirtualOffset
.y
+ it
->first
.mY
* mTileSize
.height
+
1120 tile
->mValidRect
.y
);
1121 rect
.right
= rect
.left
+ tile
->mValidRect
.width
;
1122 rect
.bottom
= rect
.top
+ tile
->mValidRect
.height
;
1124 validRects
.push_back(rect
);
1127 mVirtualSurface
->Trim(validRects
.data(), validRects
.size());
1129 // When not using a virtual surface, we still want to reset this
1130 mAllocatedRectDirty
= false;
1134 DCTile
* DCSurface::GetTile(int32_t aX
, int32_t aY
) const {
1135 TileKey
key(aX
, aY
);
1136 auto tile_it
= mDCTiles
.find(key
);
1137 MOZ_RELEASE_ASSERT(tile_it
!= mDCTiles
.end());
1138 return tile_it
->second
.get();
1141 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1142 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1144 mSwapChainBufferCount(
1145 StaticPrefs::gfx_webrender_dcomp_video_force_triple_buffering() ? 3
1149 DCSurfaceVideo::~DCSurfaceVideo() {
1150 ReleaseDecodeSwapChainResources();
1151 MOZ_ASSERT(!mSwapChainSurfaceHandle
);
1154 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat
) {
1155 if (aFormat
== DXGI_FORMAT_NV12
|| aFormat
== DXGI_FORMAT_YUY2
) {
1161 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1162 RenderTextureHost
* texture
=
1163 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1164 MOZ_RELEASE_ASSERT(texture
);
1166 if (mPrevTexture
== texture
) {
1170 // XXX if software decoded video frame format is nv12, it could be used as
1172 if (!texture
|| !texture
->AsRenderDXGITextureHost() ||
1173 texture
->GetFormat() != gfx::SurfaceFormat::NV12
) {
1174 gfxCriticalNote
<< "Unsupported RenderTexture for overlay: "
1175 << gfx::hexa(texture
);
1179 mRenderTextureHost
= texture
;
1182 static UINT
GetVendorId(ID3D11VideoDevice
* const aVideoDevice
) {
1183 RefPtr
<IDXGIDevice
> dxgiDevice
;
1184 RefPtr
<IDXGIAdapter
> adapter
;
1185 aVideoDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1186 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1188 DXGI_ADAPTER_DESC adapterDesc
;
1189 adapter
->GetDesc(&adapterDesc
);
1191 return adapterDesc
.VendorId
;
1194 static HRESULT
SetNvidiaVpSuperResolution(ID3D11VideoContext
* aVideoContext
,
1195 ID3D11VideoProcessor
* aVideoProcessor
,
1197 LOG("SetNvidiaVpSuperResolution() aEnable=%d", aEnable
);
1199 // Undocumented NVIDIA driver constants
1200 constexpr GUID nvGUID
= {0xD43CE1B3,
1203 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
1205 constexpr UINT nvExtensionVersion
= 0x1;
1206 constexpr UINT nvExtensionMethodSuperResolution
= 0x2;
1211 } streamExtensionInfo
= {nvExtensionVersion
, nvExtensionMethodSuperResolution
,
1215 hr
= aVideoContext
->VideoProcessorSetStreamExtension(
1216 aVideoProcessor
, 0, &nvGUID
, sizeof(streamExtensionInfo
),
1217 &streamExtensionInfo
);
1221 static HRESULT
SetVpSuperResolution(UINT aGpuVendorId
,
1222 ID3D11VideoContext
* aVideoContext
,
1223 ID3D11VideoProcessor
* aVideoProcessor
,
1225 MOZ_ASSERT(aVideoContext
);
1226 MOZ_ASSERT(aVideoProcessor
);
1228 if (aGpuVendorId
== 0x10DE) {
1229 return SetNvidiaVpSuperResolution(aVideoContext
, aVideoProcessor
, aEnable
);
1234 static bool GetNvidiaRTXVideoTrueHDRSupported(
1235 ID3D11VideoContext
* aVideoContext
, ID3D11VideoProcessor
* aVideoProcessor
) {
1236 const GUID kNvidiaTrueHDRInterfaceGUID
= {
1240 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
1242 HRESULT hr
= aVideoContext
->VideoProcessorGetStreamExtension(
1243 aVideoProcessor
, 0, &kNvidiaTrueHDRInterfaceGUID
, sizeof(available
),
1249 bool driverSupportsTrueHdr
= (available
== 1);
1250 return driverSupportsTrueHdr
;
1253 static HRESULT
SetNvidiaRTXVideoTrueHDR(ID3D11VideoContext
* aVideoContext
,
1254 ID3D11VideoProcessor
* aVideoProcessor
,
1256 constexpr GUID kNvidiaTrueHDRInterfaceGUID
= {
1260 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
1261 constexpr UINT kStreamExtensionMethodTrueHDR
= 0x3;
1262 const UINT TrueHDRVersion4
= 4;
1268 } streamExtensionInfo
= {TrueHDRVersion4
, kStreamExtensionMethodTrueHDR
,
1269 aEnable
? 1u : 0u, 0u};
1270 HRESULT hr
= aVideoContext
->VideoProcessorSetStreamExtension(
1271 aVideoProcessor
, 0, &kNvidiaTrueHDRInterfaceGUID
,
1272 sizeof(streamExtensionInfo
), &streamExtensionInfo
);
1276 static bool GetVpAutoHDRSupported(UINT aGpuVendorId
,
1277 ID3D11VideoContext
* aVideoContext
,
1278 ID3D11VideoProcessor
* aVideoProcessor
) {
1279 MOZ_ASSERT(aVideoContext
);
1280 MOZ_ASSERT(aVideoProcessor
);
1282 if (aGpuVendorId
== 0x10DE) {
1283 return GetNvidiaRTXVideoTrueHDRSupported(aVideoContext
, aVideoProcessor
);
1288 static HRESULT
SetVpAutoHDR(UINT aGpuVendorId
,
1289 ID3D11VideoContext
* aVideoContext
,
1290 ID3D11VideoProcessor
* aVideoProcessor
,
1292 MOZ_ASSERT(aVideoContext
);
1293 MOZ_ASSERT(aVideoProcessor
);
1295 if (aGpuVendorId
== 0x10DE) {
1296 return SetNvidiaRTXVideoTrueHDR(aVideoContext
, aVideoProcessor
, aEnable
);
1298 MOZ_ASSERT_UNREACHABLE("Unexpected to be called");
1302 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix
& aTransform
) {
1303 if (!mRenderTextureHost
) {
1304 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1308 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1309 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1310 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1311 mDCLayerTree
->SetUsedOverlayTypeInFrame(overlayType
);
1313 mVideoSize
= mRenderTextureHost
->AsRenderDXGITextureHost()->GetSize(0);
1315 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1316 // DCSurfaceVideo::PresentVideo() needs to be called.
1317 bool needsToPresent
= mPrevTexture
!= mRenderTextureHost
;
1318 gfx::IntSize swapChainSize
= mVideoSize
;
1319 gfx::Matrix transform
= aTransform
;
1320 const bool isDRM
= mRenderTextureHost
->IsFromDRMSource();
1322 // When video is rendered to axis aligned integer rectangle, video scaling
1323 // could be done by VideoProcessor
1324 bool scaleVideoAtVideoProcessor
= false;
1325 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1326 aTransform
.PreservesAxisAlignedRectangles()) {
1327 gfx::Size scaledSize
= gfx::Size(mVideoSize
) * aTransform
.ScaleFactors();
1328 gfx::IntSize
size(int32_t(std::round(scaledSize
.width
)),
1329 int32_t(std::round(scaledSize
.height
)));
1330 if (gfx::FuzzyEqual(scaledSize
.width
, size
.width
, 0.1f
) &&
1331 gfx::FuzzyEqual(scaledSize
.height
, size
.height
, 0.1f
)) {
1332 scaleVideoAtVideoProcessor
= true;
1333 swapChainSize
= size
;
1337 if (scaleVideoAtVideoProcessor
) {
1338 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1339 // subsampled formats like NV12 must have an even width and height.
1340 if (swapChainSize
.width
% 2 == 1) {
1341 swapChainSize
.width
+= 1;
1343 if (swapChainSize
.height
% 2 == 1) {
1344 swapChainSize
.height
+= 1;
1346 transform
= gfx::Matrix::Translation(aTransform
.GetTranslation());
1349 if (!mDCLayerTree
->EnsureVideoProcessor(mVideoSize
, swapChainSize
)) {
1350 gfxCriticalNote
<< "EnsureVideoProcessor Failed";
1354 MOZ_ASSERT(mDCLayerTree
->GetVideoContext());
1355 MOZ_ASSERT(mDCLayerTree
->GetVideoProcessor());
1357 const UINT vendorId
= GetVendorId(mDCLayerTree
->GetVideoDevice());
1358 const bool driverSupportsTrueHDR
=
1359 GetVpAutoHDRSupported(vendorId
, mDCLayerTree
->GetVideoContext(),
1360 mDCLayerTree
->GetVideoProcessor());
1361 const bool contentIsHDR
= false; // XXX for now, only non-HDR is supported.
1362 const bool monitorIsHDR
=
1363 gfx::DeviceManagerDx::Get()->WindowHDREnabled(mDCLayerTree
->GetHwnd());
1364 const bool powerIsCharging
= RenderThread::Get()->GetPowerIsCharging();
1366 bool useVpAutoHDR
= gfx::gfxVars::WebRenderOverlayVpAutoHDR() &&
1367 !contentIsHDR
&& monitorIsHDR
&& driverSupportsTrueHDR
&&
1368 powerIsCharging
&& !mVpAutoHDRFailed
;
1370 if (!mVideoSwapChain
|| mSwapChainSize
!= swapChainSize
|| mIsDRM
!= isDRM
||
1371 mUseVpAutoHDR
!= useVpAutoHDR
) {
1372 needsToPresent
= true;
1373 ReleaseDecodeSwapChainResources();
1374 // Update mSwapChainSize before creating SwapChain
1375 mSwapChainSize
= swapChainSize
;
1378 auto swapChainFormat
= GetSwapChainFormat(useVpAutoHDR
);
1379 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1380 if (useYUVSwapChain
) {
1381 // Tries to create YUV SwapChain
1382 CreateVideoSwapChain(swapChainFormat
);
1383 if (!mVideoSwapChain
) {
1384 mFailedYuvSwapChain
= true;
1385 ReleaseDecodeSwapChainResources();
1387 gfxCriticalNote
<< "Fallback to RGB SwapChain";
1390 // Tries to create RGB SwapChain
1391 if (!mVideoSwapChain
) {
1392 CreateVideoSwapChain(swapChainFormat
);
1394 if (!mVideoSwapChain
&& useVpAutoHDR
) {
1395 mVpAutoHDRFailed
= true;
1396 gfxCriticalNoteOnce
<< "Failed to create video SwapChain for VpAutoHDR";
1398 // Disable VpAutoHDR
1399 useVpAutoHDR
= false;
1400 swapChainFormat
= GetSwapChainFormat(useVpAutoHDR
);
1401 CreateVideoSwapChain(swapChainFormat
);
1405 aTransform
= transform
;
1406 mUseVpAutoHDR
= useVpAutoHDR
;
1408 return needsToPresent
;
1411 void DCSurfaceVideo::PresentVideo() {
1412 if (!mRenderTextureHost
) {
1416 if (!mVideoSwapChain
) {
1417 gfxCriticalNote
<< "Failed to create VideoSwapChain";
1418 RenderThread::Get()->NotifyWebRenderError(
1419 wr::WebRenderError::VIDEO_OVERLAY
);
1423 mVisual
->SetContent(mVideoSwapChain
);
1425 if (!CallVideoProcessorBlt()) {
1426 bool useYUVSwapChain
= IsYUVSwapChainFormat(mSwapChainFormat
);
1427 if (useYUVSwapChain
) {
1428 mFailedYuvSwapChain
= true;
1429 ReleaseDecodeSwapChainResources();
1432 RenderThread::Get()->NotifyWebRenderError(
1433 wr::WebRenderError::VIDEO_OVERLAY
);
1437 const auto device
= mDCLayerTree
->GetDevice();
1439 if (mFirstPresent
) {
1440 mFirstPresent
= false;
1441 UINT flags
= DXGI_PRESENT_USE_DURATION
;
1442 // DirectComposition can display black for a swap chain between the first
1443 // and second time it's presented to - maybe the first Present can get lost
1444 // somehow and it shows the wrong buffer. In that case copy the buffers so
1445 // all have the correct contents, which seems to help. The first Present()
1446 // after this needs to have SyncInterval > 0, or else the workaround doesn't
1448 for (size_t i
= 0; i
< mSwapChainBufferCount
- 1; ++i
) {
1449 hr
= mVideoSwapChain
->Present(0, flags
);
1450 // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only
1451 // indicates that the window is occluded and we can stop rendering.
1452 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1453 gfxCriticalNoteOnce
<< "video Present failed during first present: "
1458 RefPtr
<ID3D11Texture2D
> destTexture
;
1459 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1460 (void**)getter_AddRefs(destTexture
));
1461 MOZ_ASSERT(destTexture
);
1462 RefPtr
<ID3D11Texture2D
> srcTexture
;
1463 hr
= mVideoSwapChain
->GetBuffer(1, __uuidof(ID3D11Texture2D
),
1464 (void**)getter_AddRefs(srcTexture
));
1465 MOZ_ASSERT(srcTexture
);
1466 RefPtr
<ID3D11DeviceContext
> context
;
1467 device
->GetImmediateContext(getter_AddRefs(context
));
1468 MOZ_ASSERT(context
);
1469 context
->CopyResource(destTexture
, srcTexture
);
1472 // Additionally wait for the GPU to finish executing its commands, or
1473 // there still may be a black flicker when presenting expensive content
1476 RefPtr
<IDXGIDevice2
> dxgiDevice2
;
1477 device
->QueryInterface((IDXGIDevice2
**)getter_AddRefs(dxgiDevice2
));
1478 MOZ_ASSERT(dxgiDevice2
);
1480 HANDLE event
= ::CreateEvent(nullptr, false, false, nullptr);
1481 hr
= dxgiDevice2
->EnqueueSetEvent(event
);
1482 if (SUCCEEDED(hr
)) {
1483 DebugOnly
<DWORD
> result
= ::WaitForSingleObject(event
, INFINITE
);
1484 MOZ_ASSERT(result
== WAIT_OBJECT_0
);
1486 gfxCriticalNoteOnce
<< "EnqueueSetEvent failed: " << gfx::hexa(hr
);
1488 ::CloseHandle(event
);
1491 UINT flags
= DXGI_PRESENT_USE_DURATION
;
1493 if (StaticPrefs::gfx_webrender_dcomp_video_swap_chain_present_interval_0()) {
1497 auto start
= TimeStamp::Now();
1498 hr
= mVideoSwapChain
->Present(interval
, flags
);
1499 auto end
= TimeStamp::Now();
1501 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1502 gfxCriticalNoteOnce
<< "video Present failed: " << gfx::hexa(hr
);
1505 mPrevTexture
= mRenderTextureHost
;
1507 // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops
1510 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
1514 const auto maxPresentWaitDurationMs
= 2;
1515 const auto maxSlowPresentCount
= 5;
1516 const auto presentDurationMs
=
1517 static_cast<uint32_t>((end
- start
).ToMilliseconds());
1518 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1519 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1520 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1522 nsPrintfCString
marker("PresentWait overlay %u %ums ", (uint8_t)overlayType
,
1524 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS
, {}, marker
);
1526 if (presentDurationMs
> maxPresentWaitDurationMs
) {
1527 mSlowPresentCount
++;
1529 mSlowPresentCount
= 0;
1532 if (mSlowPresentCount
<= maxSlowPresentCount
) {
1536 if (overlayType
== DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
1537 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
1538 RenderThread::Get()->NotifyWebRenderError(
1539 wr::WebRenderError::VIDEO_SW_OVERLAY
);
1541 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
1542 RenderThread::Get()->NotifyWebRenderError(
1543 wr::WebRenderError::VIDEO_HW_OVERLAY
);
1547 DXGI_FORMAT
DCSurfaceVideo::GetSwapChainFormat(bool aUseVpAutoHDR
) {
1548 if (aUseVpAutoHDR
) {
1549 return DXGI_FORMAT_R16G16B16A16_FLOAT
;
1551 if (mFailedYuvSwapChain
|| !mDCLayerTree
->SupportsHardwareOverlays()) {
1552 return DXGI_FORMAT_B8G8R8A8_UNORM
;
1554 return mDCLayerTree
->GetOverlayFormatForSDR();
1557 bool DCSurfaceVideo::CreateVideoSwapChain(DXGI_FORMAT aSwapChainFormat
) {
1558 MOZ_ASSERT(mRenderTextureHost
);
1560 mFirstPresent
= true;
1562 const auto device
= mDCLayerTree
->GetDevice();
1564 RefPtr
<IDXGIDevice
> dxgiDevice
;
1565 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1567 RefPtr
<IDXGIFactoryMedia
> dxgiFactoryMedia
;
1569 RefPtr
<IDXGIAdapter
> adapter
;
1570 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1572 IID_PPV_ARGS((IDXGIFactoryMedia
**)getter_AddRefs(dxgiFactoryMedia
)));
1575 mSwapChainSurfaceHandle
= gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1576 if (!mSwapChainSurfaceHandle
) {
1577 gfxCriticalNote
<< "Failed to create DCompSurfaceHandle";
1581 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1582 desc
.Width
= mSwapChainSize
.width
;
1583 desc
.Height
= mSwapChainSize
.height
;
1584 desc
.Format
= aSwapChainFormat
;
1585 desc
.Stereo
= FALSE
;
1586 desc
.SampleDesc
.Count
= 1;
1587 desc
.BufferCount
= mSwapChainBufferCount
;
1588 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1589 desc
.Scaling
= DXGI_SCALING_STRETCH
;
1590 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1591 desc
.Flags
= DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO
;
1592 if (IsYUVSwapChainFormat(aSwapChainFormat
)) {
1593 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO
;
1596 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY
;
1598 desc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
1601 hr
= dxgiFactoryMedia
->CreateSwapChainForCompositionSurfaceHandle(
1602 device
, mSwapChainSurfaceHandle
, &desc
, nullptr,
1603 getter_AddRefs(mVideoSwapChain
));
1606 gfxCriticalNote
<< "Failed to create video SwapChain: " << gfx::hexa(hr
)
1607 << " " << mSwapChainSize
;
1611 mSwapChainFormat
= aSwapChainFormat
;
1615 // TODO: Replace with YUVRangedColorSpace
1616 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1617 const gfx::YUVColorSpace aYUVColorSpace
,
1618 const gfx::ColorRange aColorRange
) {
1619 if (aYUVColorSpace
== gfx::YUVColorSpace::BT601
) {
1620 if (aColorRange
== gfx::ColorRange::FULL
) {
1621 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601
);
1623 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
);
1625 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT709
) {
1626 if (aColorRange
== gfx::ColorRange::FULL
) {
1627 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
);
1629 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
);
1631 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT2020
) {
1632 if (aColorRange
== gfx::ColorRange::FULL
) {
1633 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1635 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020
);
1637 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
);
1644 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1645 const gfx::YUVRangedColorSpace aYUVColorSpace
) {
1646 const auto info
= FromYUVRangedColorSpace(aYUVColorSpace
);
1647 return GetSourceDXGIColorSpace(info
.space
, info
.range
);
1650 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1651 MOZ_ASSERT(mRenderTextureHost
);
1654 const auto videoDevice
= mDCLayerTree
->GetVideoDevice();
1655 const auto videoContext
= mDCLayerTree
->GetVideoContext();
1656 const auto texture
= mRenderTextureHost
->AsRenderDXGITextureHost();
1658 Maybe
<DXGI_COLOR_SPACE_TYPE
> sourceColorSpace
=
1659 GetSourceDXGIColorSpace(texture
->GetYUVColorSpace());
1660 if (sourceColorSpace
.isNothing()) {
1661 gfxCriticalNote
<< "Unsupported color space";
1665 RefPtr
<ID3D11Texture2D
> texture2D
= texture
->GetD3D11Texture2DWithGL();
1667 gfxCriticalNote
<< "Failed to get D3D11Texture2D";
1671 if (!mVideoSwapChain
) {
1675 auto query
= texture
->GetQuery();
1677 // Wait ID3D11Query of D3D11Texture2D copy complete just before blitting for
1678 // video overlay with non Intel GPUs. See Bug 1817617.
1680 bool ret
= layers::WaitForFrameGPUQuery(mDCLayerTree
->GetDevice(),
1681 mDCLayerTree
->GetDeviceContext(),
1684 gfxCriticalNoteOnce
<< "WaitForFrameGPUQuery() failed";
1688 RefPtr
<IDXGISwapChain3
> swapChain3
;
1689 mVideoSwapChain
->QueryInterface(
1690 (IDXGISwapChain3
**)getter_AddRefs(swapChain3
));
1692 gfxCriticalNote
<< "Failed to get IDXGISwapChain3";
1696 RefPtr
<ID3D11VideoContext1
> videoContext1
;
1697 videoContext
->QueryInterface(
1698 (ID3D11VideoContext1
**)getter_AddRefs(videoContext1
));
1699 if (!videoContext1
) {
1700 gfxCriticalNote
<< "Failed to get ID3D11VideoContext1";
1704 const auto videoProcessor
= mDCLayerTree
->GetVideoProcessor();
1705 const auto videoProcessorEnumerator
=
1706 mDCLayerTree
->GetVideoProcessorEnumerator();
1708 DXGI_COLOR_SPACE_TYPE inputColorSpace
= sourceColorSpace
.ref();
1709 videoContext1
->VideoProcessorSetStreamColorSpace1(videoProcessor
, 0,
1712 DXGI_COLOR_SPACE_TYPE outputColorSpace
=
1713 IsYUVSwapChainFormat(mSwapChainFormat
)
1715 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1717 if (mUseVpAutoHDR
) {
1718 outputColorSpace
= mSwapChainFormat
== DXGI_FORMAT_R16G16B16A16_FLOAT
1719 ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709
1720 : DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
;
1723 hr
= swapChain3
->SetColorSpace1(outputColorSpace
);
1725 gfxCriticalNoteOnce
<< "SetColorSpace1 failed: " << gfx::hexa(hr
);
1726 RenderThread::Get()->NotifyWebRenderError(
1727 wr::WebRenderError::VIDEO_OVERLAY
);
1730 videoContext1
->VideoProcessorSetOutputColorSpace1(videoProcessor
,
1733 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc
= {};
1734 inputDesc
.ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
;
1735 inputDesc
.Texture2D
.ArraySlice
= texture
->ArrayIndex();
1737 RefPtr
<ID3D11VideoProcessorInputView
> inputView
;
1738 hr
= videoDevice
->CreateVideoProcessorInputView(
1739 texture2D
, videoProcessorEnumerator
, &inputDesc
,
1740 getter_AddRefs(inputView
));
1742 gfxCriticalNote
<< "ID3D11VideoProcessorInputView creation failed: "
1747 D3D11_VIDEO_PROCESSOR_STREAM stream
= {};
1748 stream
.Enable
= true;
1749 stream
.OutputIndex
= 0;
1750 stream
.InputFrameOrField
= 0;
1751 stream
.PastFrames
= 0;
1752 stream
.FutureFrames
= 0;
1753 stream
.pInputSurface
= inputView
.get();
1758 destRect
.right
= mSwapChainSize
.width
;
1759 destRect
.bottom
= mSwapChainSize
.height
;
1761 videoContext
->VideoProcessorSetOutputTargetRect(videoProcessor
, TRUE
,
1763 videoContext
->VideoProcessorSetStreamDestRect(videoProcessor
, 0, TRUE
,
1766 sourceRect
.left
= 0;
1768 sourceRect
.right
= mVideoSize
.width
;
1769 sourceRect
.bottom
= mVideoSize
.height
;
1770 videoContext
->VideoProcessorSetStreamSourceRect(videoProcessor
, 0, TRUE
,
1774 RefPtr
<ID3D11Texture2D
> backBuf
;
1775 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1776 (void**)getter_AddRefs(backBuf
));
1778 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc
= {};
1779 outputDesc
.ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2D
;
1780 outputDesc
.Texture2D
.MipSlice
= 0;
1782 hr
= videoDevice
->CreateVideoProcessorOutputView(
1783 backBuf
, videoProcessorEnumerator
, &outputDesc
,
1784 getter_AddRefs(mOutputView
));
1786 gfxCriticalNote
<< "ID3D11VideoProcessorOutputView creation failed: "
1792 const UINT vendorId
= GetVendorId(videoDevice
);
1793 const auto powerIsCharging
= RenderThread::Get()->GetPowerIsCharging();
1794 if (gfx::gfxVars::WebRenderOverlayVpSuperResolution() &&
1795 !mVpSuperResolutionFailed
&& powerIsCharging
) {
1796 hr
= SetVpSuperResolution(vendorId
, videoContext
, videoProcessor
, true);
1798 if (hr
!= E_NOTIMPL
) {
1799 gfxCriticalNoteOnce
<< "SetVpSuperResolution failed: " << gfx::hexa(hr
);
1801 mVpSuperResolutionFailed
= true;
1805 if (mUseVpAutoHDR
) {
1806 hr
= SetVpAutoHDR(vendorId
, videoContext
, videoProcessor
, true);
1808 gfxCriticalNoteOnce
<< "SetVpAutoHDR failed: " << gfx::hexa(hr
);
1809 mVpAutoHDRFailed
= true;
1813 hr
= videoContext
->VideoProcessorBlt(videoProcessor
, mOutputView
, 0, 1,
1816 gfxCriticalNote
<< "VideoProcessorBlt failed: " << gfx::hexa(hr
);
1823 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1824 mOutputView
= nullptr;
1825 mVideoSwapChain
= nullptr;
1826 mDecodeSwapChain
= nullptr;
1827 mDecodeResource
= nullptr;
1828 if (mSwapChainSurfaceHandle
) {
1829 ::CloseHandle(mSwapChainSurfaceHandle
);
1830 mSwapChainSurfaceHandle
= 0;
1832 mUseVpAutoHDR
= false;
1835 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1836 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1839 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1840 RenderTextureHost
* texture
=
1841 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1842 RenderDcompSurfaceTextureHost
* renderTexture
=
1843 texture
? texture
->AsRenderDcompSurfaceTextureHost() : nullptr;
1844 if (!renderTexture
) {
1845 gfxCriticalNote
<< "Unsupported RenderTexture for DCSurfaceHandle: "
1846 << gfx::hexa(texture
);
1850 const auto handle
= renderTexture
->GetDcompSurfaceHandle();
1851 if (GetSurfaceHandle() == handle
) {
1855 LOG_H("AttachExternalImage, ext-image=%" PRIu64
", texture=%p, handle=%p",
1856 wr::AsUint64(aExternalImage
), renderTexture
, handle
);
1857 mDcompTextureHost
= renderTexture
;
1860 HANDLE
DCSurfaceHandle::GetSurfaceHandle() const {
1861 if (mDcompTextureHost
) {
1862 return mDcompTextureHost
->GetDcompSurfaceHandle();
1867 IDCompositionSurface
* DCSurfaceHandle::EnsureSurface() {
1868 if (auto* surface
= mDcompTextureHost
->GetSurface()) {
1872 // Texture host hasn't created the surface yet, ask it to create a new one.
1873 RefPtr
<IDCompositionDevice
> device
;
1874 HRESULT hr
= mDCLayerTree
->GetCompositionDevice()->QueryInterface(
1875 (IDCompositionDevice
**)getter_AddRefs(device
));
1878 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1883 return mDcompTextureHost
->CreateSurfaceFromDevice(device
);
1886 void DCSurfaceHandle::PresentSurfaceHandle() {
1887 LOG_H("PresentSurfaceHandle");
1888 if (IDCompositionSurface
* surface
= EnsureSurface()) {
1889 LOG_H("Set surface %p to visual", surface
);
1890 mVisual
->SetContent(surface
);
1892 mVisual
->SetContent(nullptr);
1896 DCTile::DCTile(DCLayerTree
* aDCLayerTree
) : mDCLayerTree(aDCLayerTree
) {}
1898 DCTile::~DCTile() {}
1900 bool DCTile::Initialize(int aX
, int aY
, wr::DeviceIntSize aSize
,
1901 bool aIsVirtualSurface
, bool aIsOpaque
,
1902 RefPtr
<IDCompositionVisual2
> mSurfaceVisual
) {
1903 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
1908 mIsOpaque
= aIsOpaque
;
1909 mIsVirtualSurface
= aIsVirtualSurface
;
1910 mNeedsFullDraw
= !aIsVirtualSurface
;
1912 if (aIsVirtualSurface
) {
1913 // Initially, the entire tile is considered valid, unless it is set by
1914 // the SetTileProperties method.
1917 mValidRect
.width
= aSize
.width
;
1918 mValidRect
.height
= aSize
.height
;
1921 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1922 // Create the visual and put it in the tree under the surface visual
1923 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1925 gfxCriticalNote
<< "Failed to CreateVisual for DCTile: " << gfx::hexa(hr
);
1928 mSurfaceVisual
->AddVisual(mVisual
, false, nullptr);
1929 // Position the tile relative to the surface visual
1930 mVisual
->SetOffsetX(aX
* aSize
.width
);
1931 mVisual
->SetOffsetY(aY
* aSize
.height
);
1932 // Clip the visual so it doesn't show anything until we update it
1933 D2D_RECT_F clip
= {0, 0, 0, 0};
1934 mVisual
->SetClip(clip
);
1935 // Create the underlying pixel buffer.
1936 mCompositionSurface
= CreateCompositionSurface(aSize
, aIsOpaque
);
1937 if (!mCompositionSurface
) {
1940 hr
= mVisual
->SetContent(mCompositionSurface
);
1942 gfxCriticalNote
<< "Failed to SetContent for DCTile: " << gfx::hexa(hr
);
1950 RefPtr
<IDCompositionSurface
> DCTile::CreateCompositionSurface(
1951 wr::DeviceIntSize aSize
, bool aIsOpaque
) {
1953 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1954 const auto alphaMode
=
1955 aIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1956 RefPtr
<IDCompositionSurface
> compositionSurface
;
1958 hr
= dCompDevice
->CreateSurface(aSize
.width
, aSize
.height
,
1959 DXGI_FORMAT_R8G8B8A8_UNORM
, alphaMode
,
1960 getter_AddRefs(compositionSurface
));
1962 gfxCriticalNote
<< "Failed to CreateSurface for DCTile: " << gfx::hexa(hr
);
1965 return compositionSurface
;
1968 RefPtr
<IDCompositionSurface
> DCTile::Bind(wr::DeviceIntRect aValidRect
) {
1969 if (mVisual
!= nullptr) {
1970 // Tile owns a visual, set the size of the visual to match the portion we
1971 // want to be visible.
1972 D2D_RECT_F clip_rect
;
1973 clip_rect
.left
= aValidRect
.min
.x
;
1974 clip_rect
.top
= aValidRect
.min
.y
;
1975 clip_rect
.right
= aValidRect
.max
.x
;
1976 clip_rect
.bottom
= aValidRect
.max
.y
;
1977 mVisual
->SetClip(clip_rect
);
1979 return mCompositionSurface
;
1982 GLuint
DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1983 wr::DeviceIntRect aDirtyRect
, wr::DeviceIntPoint
* aOffset
,
1984 RefPtr
<IDCompositionSurface
> aCompositionSurface
,
1985 wr::DeviceIntPoint aSurfaceOffset
) {
1986 MOZ_ASSERT(aCompositionSurface
.get());
1989 const auto gl
= GetGLContext();
1990 RefPtr
<ID3D11Texture2D
> backBuf
;
1994 update_rect
.left
= aSurfaceOffset
.x
+ aDirtyRect
.min
.x
;
1995 update_rect
.top
= aSurfaceOffset
.y
+ aDirtyRect
.min
.y
;
1996 update_rect
.right
= aSurfaceOffset
.x
+ aDirtyRect
.max
.x
;
1997 update_rect
.bottom
= aSurfaceOffset
.y
+ aDirtyRect
.max
.y
;
1998 hr
= aCompositionSurface
->BeginDraw(&update_rect
, __uuidof(ID3D11Texture2D
),
1999 (void**)getter_AddRefs(backBuf
), &offset
);
2002 LayoutDeviceIntRect rect
= widget::WinUtils::ToIntRect(update_rect
);
2004 gfxCriticalNote
<< "DCompositionSurface::BeginDraw failed: "
2005 << gfx::hexa(hr
) << " " << rect
;
2006 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW
);
2010 // DC includes the origin of the dirty / update rect in the draw offset,
2011 // undo that here since WR expects it to be an absolute offset.
2012 offset
.x
-= aDirtyRect
.min
.x
;
2013 offset
.y
-= aDirtyRect
.min
.y
;
2015 D3D11_TEXTURE2D_DESC desc
;
2016 backBuf
->GetDesc(&desc
);
2018 const auto& gle
= gl::GLContextEGL::Cast(gl
);
2019 const auto& egl
= gle
->mEgl
;
2021 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
2023 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
2024 const EGLint attribs
[] = {LOCAL_EGL_NONE
};
2025 mEGLImage
= egl
->fCreateImage(EGL_NO_CONTEXT
, LOCAL_EGL_D3D11_TEXTURE_ANGLE
,
2028 // Get the current FBO and RBO id, so we can restore them later
2029 GLint currentFboId
, currentRboId
;
2030 gl
->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
, ¤tFboId
);
2031 gl
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, ¤tRboId
);
2033 // Create a render buffer object that is backed by the EGL image.
2034 gl
->fGenRenderbuffers(1, &mColorRBO
);
2035 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, mColorRBO
);
2036 gl
->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, mEGLImage
);
2038 // Get or create an FBO for the specified dimensions
2039 GLuint fboId
= GetOrCreateFbo(desc
.Width
, desc
.Height
);
2041 // Attach the new renderbuffer to the FBO
2042 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
2043 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
2044 LOCAL_GL_COLOR_ATTACHMENT0
,
2045 LOCAL_GL_RENDERBUFFER
, mColorRBO
);
2047 // Restore previous FBO and RBO bindings
2048 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, currentFboId
);
2049 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, currentRboId
);
2051 aOffset
->x
= offset
.x
;
2052 aOffset
->y
= offset
.y
;
2057 void DCLayerTree::DestroyEGLSurface() {
2058 const auto gl
= GetGLContext();
2061 gl
->fDeleteRenderbuffers(1, &mColorRBO
);
2066 const auto& gle
= gl::GLContextEGL::Cast(gl
);
2067 const auto& egl
= gle
->mEgl
;
2068 egl
->fDestroyImage(mEGLImage
);
2069 mEGLImage
= EGL_NO_IMAGE
;
2075 color::ColorProfileDesc
DCLayerTree::QueryOutputColorProfile() {
2076 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
2077 // but we do need gfxPlatform::GetCMSOutputProfile().
2078 // So we steal what we need through the window:
2079 const auto outputProfileData
=
2080 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
2082 const auto qcmsProfile
= qcms_profile_from_memory(
2083 outputProfileData
.Elements(), outputProfileData
.Length());
2084 const auto release
= MakeScopeExit([&]() {
2086 qcms_profile_release(qcmsProfile
);
2090 const bool print
= gfxEnv::MOZ_GL_SPEW();
2092 const auto ret
= [&]() {
2094 return color::ColorProfileDesc::From(*qcmsProfile
);
2098 "Missing or failed to load display color profile, defaulting to "
2101 const auto MISSING_PROFILE_DEFAULT_SPACE
= color::ColorspaceDesc
{
2102 color::Chromaticities::Srgb(),
2103 color::PiecewiseGammaDesc::Srgb(),
2105 return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE
);
2109 const auto gammaGuess
= color::GuessGamma(ret
.linearFromTf
.r
);
2111 "Display profile:\n"
2112 " Approx Gamma: %f\n"
2113 " XYZ-D65 Red : %f, %f, %f\n"
2114 " XYZ-D65 Green: %f, %f, %f\n"
2115 " XYZ-D65 Blue : %f, %f, %f\n",
2116 gammaGuess
, ret
.xyzd65FromLinearRgb
.at(0, 0),
2117 ret
.xyzd65FromLinearRgb
.at(0, 1), ret
.xyzd65FromLinearRgb
.at(0, 2),
2119 ret
.xyzd65FromLinearRgb
.at(1, 0), ret
.xyzd65FromLinearRgb
.at(1, 1),
2120 ret
.xyzd65FromLinearRgb
.at(1, 2),
2122 ret
.xyzd65FromLinearRgb
.at(2, 0), ret
.xyzd65FromLinearRgb
.at(2, 1),
2123 ret
.xyzd65FromLinearRgb
.at(2, 2));
2129 inline D2D1_MATRIX_5X4_F
to_D2D1_MATRIX_5X4_F(const color::mat4
& m
) {
2130 return D2D1_MATRIX_5X4_F
{{{
2154 ColorManagementChain
ColorManagementChain::From(
2155 IDCompositionDevice3
& dcomp
,
2156 const color::ColorProfileConversionDesc
& conv
) {
2157 auto ret
= ColorManagementChain
{};
2159 #if !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
2161 const auto Append
= [&](const RefPtr
<IDCompositionFilterEffect
>& afterLast
) {
2163 afterLast
->SetInput(0, ret
.last
, 0);
2165 ret
.last
= afterLast
;
2168 const auto MaybeAppendColorMatrix
= [&](const color::mat4
& m
) {
2169 RefPtr
<IDCompositionColorMatrixEffect
> e
;
2170 if (approx(m
, color::mat4::Identity())) return e
;
2171 dcomp
.CreateColorMatrixEffect(getter_AddRefs(e
));
2174 e
->SetMatrix(to_D2D1_MATRIX_5X4_F(m
));
2178 const auto MaybeAppendTableTransfer
= [&](const color::RgbTransferTables
& t
) {
2179 RefPtr
<IDCompositionTableTransferEffect
> e
;
2180 if (!t
.r
.size() && !t
.g
.size() && !t
.b
.size()) return e
;
2181 dcomp
.CreateTableTransferEffect(getter_AddRefs(e
));
2184 e
->SetRedTable(t
.r
.data(), t
.r
.size());
2185 e
->SetGreenTable(t
.g
.data(), t
.g
.size());
2186 e
->SetBlueTable(t
.b
.data(), t
.b
.size());
2191 ret
.srcRgbFromSrcYuv
= MaybeAppendColorMatrix(conv
.srcRgbFromSrcYuv
);
2192 ret
.srcLinearFromSrcTf
= MaybeAppendTableTransfer(conv
.srcLinearFromSrcTf
);
2193 ret
.dstLinearFromSrcLinear
=
2194 MaybeAppendColorMatrix(color::mat4(conv
.dstLinearFromSrcLinear
));
2195 ret
.dstTfFromDstLinear
= MaybeAppendTableTransfer(conv
.dstTfFromDstLinear
);
2197 #endif // !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
2202 ColorManagementChain::~ColorManagementChain() = default;
2205 } // namespace mozilla