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/StaticPrefs_gfx.h"
27 #include "mozilla/webrender/RenderD3D11TextureHost.h"
28 #include "mozilla/webrender/RenderDcompSurfaceTextureHost.h"
29 #include "mozilla/webrender/RenderTextureHost.h"
30 #include "mozilla/webrender/RenderThread.h"
31 #include "mozilla/WindowsVersion.h"
32 #include "mozilla/Telemetry.h"
33 #include "nsPrintfCString.h"
38 #if defined(__MINGW32__) // 64 defines both 32 and 64
39 // We need to fake some things, while we wait on updates to mingw's dcomp.h
40 // header. Just enough that we can successfully fail to work there.
41 # define MOZ_MINGW_DCOMP_H_INCOMPLETE
42 struct IDCompositionColorMatrixEffect
: public IDCompositionFilterEffect
{};
43 struct IDCompositionTableTransferEffect
: public IDCompositionFilterEffect
{};
44 #endif // defined(__MINGW32__)
49 extern LazyLogModule gRenderThreadLog
;
50 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
52 #define LOG_H(msg, ...) \
53 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
54 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
56 UniquePtr
<GpuOverlayInfo
> DCLayerTree::sGpuOverlayInfo
;
59 UniquePtr
<DCLayerTree
> DCLayerTree::Create(gl::GLContext
* aGL
,
61 ID3D11Device
* aDevice
,
62 ID3D11DeviceContext
* aCtx
,
63 HWND aHwnd
, nsACString
& aError
) {
64 RefPtr
<IDCompositionDevice2
> dCompDevice
=
65 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
67 aError
.Assign("DCLayerTree(no device)"_ns
);
72 MakeUnique
<DCLayerTree
>(aGL
, aEGLConfig
, aDevice
, aCtx
, dCompDevice
);
73 if (!layerTree
->Initialize(aHwnd
, aError
)) {
80 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo
= nullptr; }
82 DCLayerTree::DCLayerTree(gl::GLContext
* aGL
, EGLConfig aEGLConfig
,
83 ID3D11Device
* aDevice
, ID3D11DeviceContext
* aCtx
,
84 IDCompositionDevice2
* aCompositionDevice
)
86 mEGLConfig(aEGLConfig
),
89 mCompositionDevice(aCompositionDevice
),
91 mDebugVisualRedrawRegions(false),
92 mEGLImage(EGL_NO_IMAGE
),
94 mPendingCommit(false) {
95 LOG("DCLayerTree::DCLayerTree()");
98 DCLayerTree::~DCLayerTree() {
99 LOG("DCLayerTree::~DCLayerTree()");
101 ReleaseNativeCompositorResources();
104 void DCLayerTree::ReleaseNativeCompositorResources() {
105 const auto gl
= GetGLContext();
109 // Delete any cached FBO objects
110 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
111 gl
->fDeleteRenderbuffers(1, &it
->depthRboId
);
112 gl
->fDeleteFramebuffers(1, &it
->fboId
);
116 bool DCLayerTree::Initialize(HWND aHwnd
, nsACString
& aError
) {
119 RefPtr
<IDCompositionDesktopDevice
> desktopDevice
;
120 hr
= mCompositionDevice
->QueryInterface(
121 (IDCompositionDesktopDevice
**)getter_AddRefs(desktopDevice
));
123 aError
.Assign(nsPrintfCString(
124 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr
));
128 hr
= desktopDevice
->CreateTargetForHwnd(aHwnd
, TRUE
,
129 getter_AddRefs(mCompositionTarget
));
131 aError
.Assign(nsPrintfCString(
132 "DCLayerTree(create DCompositionTarget failed %lx)", hr
));
136 hr
= mCompositionDevice
->CreateVisual(getter_AddRefs(mRootVisual
));
138 aError
.Assign(nsPrintfCString(
139 "DCLayerTree(create root DCompositionVisual failed %lx)", hr
));
144 mCompositionDevice
->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual
));
146 aError
.Assign(nsPrintfCString(
147 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr
));
151 if (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin() ||
152 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) {
153 if (!InitializeVideoOverlaySupport()) {
154 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY
);
157 if (!sGpuOverlayInfo
) {
158 // Set default if sGpuOverlayInfo was not set.
159 sGpuOverlayInfo
= MakeUnique
<GpuOverlayInfo
>();
162 // Initialize SwapChainInfo
163 SupportsSwapChainTearing();
165 mCompositionTarget
->SetRoot(mRootVisual
);
166 // Set interporation mode to nearest, to ensure 1:1 sampling.
167 // By default, a visual inherits the interpolation mode of the parent visual.
168 // If no visuals set the interpolation mode, the default for the entire visual
169 // tree is nearest neighbor interpolation.
170 mRootVisual
->SetBitmapInterpolationMode(
171 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
175 bool FlagsSupportsOverlays(UINT flags
) {
176 return (flags
& (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
|
177 DXGI_OVERLAY_SUPPORT_FLAG_SCALING
));
180 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
181 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat
,
182 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace
,
183 RefPtr
<IDXGIOutput
> aOutput
,
184 RefPtr
<ID3D11Device
> aD3d11Device
) {
185 UINT colorSpaceSupportFlags
= 0;
186 RefPtr
<IDXGIOutput4
> output4
;
188 if (FAILED(aOutput
->QueryInterface(__uuidof(IDXGIOutput4
),
189 getter_AddRefs(output4
)))) {
193 if (FAILED(output4
->CheckOverlayColorSpaceSupport(
194 aDxgiFormat
, aDxgiColorSpace
, aD3d11Device
,
195 &colorSpaceSupportFlags
))) {
199 return (colorSpaceSupportFlags
&
200 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT
);
203 bool DCLayerTree::InitializeVideoOverlaySupport() {
204 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
208 hr
= mDevice
->QueryInterface(
209 (ID3D11VideoDevice
**)getter_AddRefs(mVideoDevice
));
211 gfxCriticalNote
<< "Failed to get D3D11VideoDevice: " << gfx::hexa(hr
);
216 mCtx
->QueryInterface((ID3D11VideoContext
**)getter_AddRefs(mVideoContext
));
218 gfxCriticalNote
<< "Failed to get D3D11VideoContext: " << gfx::hexa(hr
);
222 if (sGpuOverlayInfo
) {
226 UniquePtr
<GpuOverlayInfo
> info
= MakeUnique
<GpuOverlayInfo
>();
228 RefPtr
<IDXGIDevice
> dxgiDevice
;
229 RefPtr
<IDXGIAdapter
> adapter
;
230 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
231 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
235 RefPtr
<IDXGIOutput
> output
;
236 if (FAILED(adapter
->EnumOutputs(i
++, getter_AddRefs(output
)))) {
239 RefPtr
<IDXGIOutput3
> output3
;
240 if (FAILED(output
->QueryInterface(__uuidof(IDXGIOutput3
),
241 getter_AddRefs(output3
)))) {
245 output3
->CheckOverlaySupport(DXGI_FORMAT_NV12
, mDevice
,
246 &info
->mNv12OverlaySupportFlags
);
247 output3
->CheckOverlaySupport(DXGI_FORMAT_YUY2
, mDevice
,
248 &info
->mYuy2OverlaySupportFlags
);
249 output3
->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM
, mDevice
,
250 &info
->mBgra8OverlaySupportFlags
);
251 output3
->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM
, mDevice
,
252 &info
->mRgb10a2OverlaySupportFlags
);
254 if (FlagsSupportsOverlays(info
->mNv12OverlaySupportFlags
)) {
255 // NV12 format is preferred if it's supported.
256 info
->mOverlayFormatUsed
= DXGI_FORMAT_NV12
;
257 info
->mSupportsHardwareOverlays
= true;
260 if (!info
->mSupportsHardwareOverlays
&&
261 FlagsSupportsOverlays(info
->mYuy2OverlaySupportFlags
)) {
262 // If NV12 isn't supported, fallback to YUY2 if it's supported.
263 info
->mOverlayFormatUsed
= DXGI_FORMAT_YUY2
;
264 info
->mSupportsHardwareOverlays
= true;
267 // RGB10A2 overlay is used for displaying HDR content. In Intel's
268 // platform, RGB10A2 overlay is enabled only when
269 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
270 if (FlagsSupportsOverlays(info
->mRgb10a2OverlaySupportFlags
)) {
271 if (!CheckOverlayColorSpaceSupport(
272 DXGI_FORMAT_R10G10B10A2_UNORM
,
273 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
, output
, mDevice
))
274 info
->mRgb10a2OverlaySupportFlags
= 0;
277 // Early out after the first output that reports overlay support. All
278 // outputs are expected to report the same overlay support according to
279 // Microsoft's WDDM documentation:
280 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
281 if (info
->mSupportsHardwareOverlays
) {
286 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
287 info
->mOverlayFormatUsed
= DXGI_FORMAT_B8G8R8A8_UNORM
;
288 info
->mSupportsHardwareOverlays
= false;
291 info
->mSupportsOverlays
= info
->mSupportsHardwareOverlays
;
293 sGpuOverlayInfo
= std::move(info
);
295 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
296 gpuParent
->NotifyOverlayInfo(GetOverlayInfo());
302 DCSurface
* DCLayerTree::GetSurface(wr::NativeSurfaceId aId
) const {
303 auto surface_it
= mDCSurfaces
.find(aId
);
304 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
305 return surface_it
->second
.get();
308 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1
* aSwapChain
) {
309 LOG("DCLayerTree::SetDefaultSwapChain()");
311 mRootVisual
->AddVisual(mDefaultSwapChainVisual
, TRUE
, nullptr);
312 mDefaultSwapChainVisual
->SetContent(aSwapChain
);
313 // Default SwapChain's visual does not need linear interporation.
314 mDefaultSwapChainVisual
->SetBitmapInterpolationMode(
315 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
316 mPendingCommit
= true;
319 void DCLayerTree::MaybeUpdateDebug() {
320 bool updated
= false;
321 updated
|= MaybeUpdateDebugCounter();
322 updated
|= MaybeUpdateDebugVisualRedrawRegions();
324 mPendingCommit
= true;
328 void DCLayerTree::MaybeCommit() {
329 if (!mPendingCommit
) {
332 mCompositionDevice
->Commit();
335 void DCLayerTree::WaitForCommitCompletion() {
336 mCompositionDevice
->WaitForCommitCompletion();
339 void DCLayerTree::DisableNativeCompositor() {
340 MOZ_ASSERT(mCurrentSurface
.isNothing());
341 MOZ_ASSERT(mCurrentLayers
.empty());
343 ReleaseNativeCompositorResources();
345 mRootVisual
->RemoveAllVisuals();
348 bool DCLayerTree::MaybeUpdateDebugCounter() {
349 bool debugCounter
= StaticPrefs::gfx_webrender_debug_dcomp_counter();
350 if (mDebugCounter
== debugCounter
) {
354 RefPtr
<IDCompositionDeviceDebug
> debugDevice
;
355 HRESULT hr
= mCompositionDevice
->QueryInterface(
356 (IDCompositionDeviceDebug
**)getter_AddRefs(debugDevice
));
362 debugDevice
->EnableDebugCounters();
364 debugDevice
->DisableDebugCounters();
367 mDebugCounter
= debugCounter
;
371 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
372 bool debugVisualRedrawRegions
=
373 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
374 if (mDebugVisualRedrawRegions
== debugVisualRedrawRegions
) {
378 RefPtr
<IDCompositionVisualDebug
> visualDebug
;
379 HRESULT hr
= mRootVisual
->QueryInterface(
380 (IDCompositionVisualDebug
**)getter_AddRefs(visualDebug
));
385 if (debugVisualRedrawRegions
) {
386 visualDebug
->EnableRedrawRegions();
388 visualDebug
->DisableRedrawRegions();
391 mDebugVisualRedrawRegions
= debugVisualRedrawRegions
;
395 void DCLayerTree::CompositorBeginFrame() {
397 mUsedOverlayTypesInFrame
= DCompOverlayTypes::NO_OVERLAY
;
400 void DCLayerTree::CompositorEndFrame() {
401 auto start
= TimeStamp::Now();
402 // Check if the visual tree of surfaces is the same as last frame.
403 bool same
= mPrevLayers
== mCurrentLayers
;
406 // If not, we need to rebuild the visual tree. Note that addition or
407 // removal of tiles no longer needs to rebuild the main visual tree
408 // here, since they are added as children of the surface visual.
409 mRootVisual
->RemoveAllVisuals();
412 for (auto it
= mCurrentLayers
.begin(); it
!= mCurrentLayers
.end(); ++it
) {
413 auto surface_it
= mDCSurfaces
.find(*it
);
414 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
415 const auto surface
= surface_it
->second
.get();
416 // Ensure surface is trimmed to updated tile valid rects
417 surface
->UpdateAllocatedRect();
419 // Add surfaces in z-order they were added to the scene.
420 const auto visual
= surface
->GetVisual();
421 mRootVisual
->AddVisual(visual
, false, nullptr);
425 mPrevLayers
.swap(mCurrentLayers
);
426 mCurrentLayers
.clear();
428 mCompositionDevice
->Commit();
430 auto end
= TimeStamp::Now();
431 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
432 (end
- start
).ToMilliseconds() * 10.);
434 // Remove any framebuffers that haven't been
435 // used in the last 60 frames.
437 // This should use nsTArray::RemoveElementsBy once
438 // CachedFrameBuffer is able to properly destroy
439 // itself in the destructor.
440 const auto gl
= GetGLContext();
441 for (uint32_t i
= 0, len
= mFrameBuffers
.Length(); i
< len
; ++i
) {
442 auto& fb
= mFrameBuffers
[i
];
443 if ((mCurrentFrame
- fb
.lastFrameUsed
) > 60) {
444 gl
->fDeleteRenderbuffers(1, &fb
.depthRboId
);
445 gl
->fDeleteFramebuffers(1, &fb
.fboId
);
446 mFrameBuffers
.UnorderedRemoveElementAt(i
);
447 --i
; // Examine the element again, if necessary.
452 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
456 // Disable video overlay if mCompositionDevice->Commit() with video overlay is
457 // too slow. It drops fps.
459 const auto maxCommitWaitDurationMs
= 20;
460 const auto maxSlowCommitCount
= 5;
461 const auto commitDurationMs
=
462 static_cast<uint32_t>((end
- start
).ToMilliseconds());
464 nsPrintfCString
marker("CommitWait overlay %u %ums ",
465 (uint8_t)mUsedOverlayTypesInFrame
, commitDurationMs
);
466 PROFILER_MARKER_TEXT("CommitWait", GRAPHICS
, {}, marker
);
468 if (mUsedOverlayTypesInFrame
!= DCompOverlayTypes::NO_OVERLAY
&&
469 commitDurationMs
> maxCommitWaitDurationMs
) {
472 mSlowCommitCount
= 0;
475 if (mSlowCommitCount
<= maxSlowCommitCount
) {
479 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
480 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
481 RenderThread::Get()->NotifyWebRenderError(
482 wr::WebRenderError::VIDEO_SW_OVERLAY
);
484 if (mUsedOverlayTypesInFrame
& DCompOverlayTypes::HARDWARE_DECODED_VIDEO
) {
485 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
486 RenderThread::Get()->NotifyWebRenderError(
487 wr::WebRenderError::VIDEO_HW_OVERLAY
);
491 void DCLayerTree::Bind(wr::NativeTileId aId
, wr::DeviceIntPoint
* aOffset
,
492 uint32_t* aFboId
, wr::DeviceIntRect aDirtyRect
,
493 wr::DeviceIntRect aValidRect
) {
494 auto surface
= GetSurface(aId
.surface_id
);
495 auto tile
= surface
->GetTile(aId
.x
, aId
.y
);
496 wr::DeviceIntPoint targetOffset
{0, 0};
498 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
499 // IDCompositionVirtualSurface owned by the DCSurface.
500 RefPtr
<IDCompositionSurface
> compositionSurface
;
501 if (surface
->mIsVirtualSurface
) {
502 gfx::IntRect
validRect(aValidRect
.min
.x
, aValidRect
.min
.y
,
503 aValidRect
.width(), aValidRect
.height());
504 if (!tile
->mValidRect
.IsEqualEdges(validRect
)) {
505 tile
->mValidRect
= validRect
;
506 surface
->DirtyAllocatedRect();
508 wr::DeviceIntSize tileSize
= surface
->GetTileSize();
509 compositionSurface
= surface
->GetCompositionSurface();
510 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
511 targetOffset
.x
= virtualOffset
.x
+ tileSize
.width
* aId
.x
;
512 targetOffset
.y
= virtualOffset
.y
+ tileSize
.height
* aId
.y
;
514 compositionSurface
= tile
->Bind(aValidRect
);
517 if (tile
->mNeedsFullDraw
) {
518 // dcomp requires that the first BeginDraw on a non-virtual surface is the
519 // full size of the pixel buffer.
520 auto tileSize
= surface
->GetTileSize();
521 aDirtyRect
.min
.x
= 0;
522 aDirtyRect
.min
.y
= 0;
523 aDirtyRect
.max
.x
= tileSize
.width
;
524 aDirtyRect
.max
.y
= tileSize
.height
;
525 tile
->mNeedsFullDraw
= false;
528 *aFboId
= CreateEGLSurfaceForCompositionSurface(
529 aDirtyRect
, aOffset
, compositionSurface
, targetOffset
);
530 mCurrentSurface
= Some(compositionSurface
);
533 void DCLayerTree::Unbind() {
534 if (mCurrentSurface
.isNothing()) {
538 RefPtr
<IDCompositionSurface
> surface
= mCurrentSurface
.ref();
542 mCurrentSurface
= Nothing();
545 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId
,
546 wr::DeviceIntPoint aVirtualOffset
,
547 wr::DeviceIntSize aTileSize
, bool aIsOpaque
) {
548 auto it
= mDCSurfaces
.find(aId
);
549 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
550 if (it
!= mDCSurfaces
.end()) {
551 // DCSurface already exists.
555 // Tile size needs to be positive.
556 if (aTileSize
.width
<= 0 || aTileSize
.height
<= 0) {
557 gfxCriticalNote
<< "TileSize is not positive aId: " << wr::AsUint64(aId
)
558 << " aTileSize(" << aTileSize
.width
<< ","
559 << aTileSize
.height
<< ")";
562 bool isVirtualSurface
=
563 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
564 auto surface
= MakeUnique
<DCSurface
>(aTileSize
, aVirtualOffset
,
565 isVirtualSurface
, aIsOpaque
, this);
566 if (!surface
->Initialize()) {
567 gfxCriticalNote
<< "Failed to initialize DCSurface: " << wr::AsUint64(aId
);
571 mDCSurfaces
[aId
] = std::move(surface
);
574 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId
,
576 auto it
= mDCSurfaces
.find(aId
);
577 MOZ_RELEASE_ASSERT(it
== mDCSurfaces
.end());
579 auto surface
= MakeUnique
<DCExternalSurfaceWrapper
>(aIsOpaque
, this);
580 if (!surface
->Initialize()) {
581 gfxCriticalNote
<< "Failed to initialize DCExternalSurfaceWrapper: "
582 << wr::AsUint64(aId
);
586 mDCSurfaces
[aId
] = std::move(surface
);
589 void DCLayerTree::DestroySurface(NativeSurfaceId aId
) {
590 auto surface_it
= mDCSurfaces
.find(aId
);
591 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
592 auto surface
= surface_it
->second
.get();
594 mRootVisual
->RemoveVisual(surface
->GetVisual());
595 mDCSurfaces
.erase(surface_it
);
598 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
599 auto surface
= GetSurface(aId
);
600 surface
->CreateTile(aX
, aY
);
603 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId
, int32_t aX
, int32_t aY
) {
604 auto surface
= GetSurface(aId
);
605 surface
->DestroyTile(aX
, aY
);
608 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId
,
609 wr::ExternalImageId aExternalImage
) {
610 auto surface_it
= mDCSurfaces
.find(aId
);
611 MOZ_RELEASE_ASSERT(surface_it
!= mDCSurfaces
.end());
612 surface_it
->second
->AttachExternalImage(aExternalImage
);
615 void DCExternalSurfaceWrapper::AttachExternalImage(
616 wr::ExternalImageId aExternalImage
) {
617 if (auto* surface
= EnsureSurfaceForExternalImage(aExternalImage
)) {
618 surface
->AttachExternalImage(aExternalImage
);
624 template <class FromT
>
625 [[nodiscard
]] static inline RefPtr
<ToT
> From(FromT
* const from
) {
627 (void)from
->QueryInterface(static_cast<ToT
**>(getter_AddRefs(to
)));
632 DCSurface
* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
633 wr::ExternalImageId aExternalImage
) {
635 return mSurface
.get();
638 // Create a new surface based on the texture type.
639 RenderTextureHost
* texture
=
640 RenderThread::Get()->GetRenderTexture(aExternalImage
);
641 if (texture
&& texture
->AsRenderDXGITextureHost()) {
642 mSurface
.reset(new DCSurfaceVideo(mIsOpaque
, mDCLayerTree
));
643 if (!mSurface
->Initialize()) {
644 gfxCriticalNote
<< "Failed to initialize DCSurfaceVideo: "
645 << wr::AsUint64(aExternalImage
);
648 } else if (texture
&& texture
->AsRenderDcompSurfaceTextureHost()) {
649 mSurface
.reset(new DCSurfaceHandle(mIsOpaque
, mDCLayerTree
));
650 if (!mSurface
->Initialize()) {
651 gfxCriticalNote
<< "Failed to initialize DCSurfaceHandle: "
652 << wr::AsUint64(aExternalImage
);
657 gfxCriticalNote
<< "Failed to create a surface for external image: "
658 << gfx::hexa(texture
);
662 // Add surface's visual which will contain video data to our root visual.
663 const auto surfaceVisual
= mSurface
->GetVisual();
664 mVisual
->AddVisual(surfaceVisual
, true, nullptr);
667 // Apply color management.
670 const auto cmsMode
= GfxColorManagementMode();
671 if (cmsMode
== CMSMode::Off
) return;
673 const auto dcomp
= mDCLayerTree
->GetCompositionDevice();
674 const auto dcomp3
= QI
<IDCompositionDevice3
>::From(dcomp
);
677 "No IDCompositionDevice3, cannot use dcomp for color management.");
683 const auto cspace
= [&]() {
684 const auto rangedCspace
= texture
->GetYUVColorSpace();
685 const auto info
= FromYUVRangedColorSpace(rangedCspace
);
686 auto ret
= ToColorSpace2(info
.space
);
687 if (ret
== gfx::ColorSpace2::Display
&& cmsMode
== CMSMode::All
) {
688 ret
= gfx::ColorSpace2::SRGB
;
693 const bool rec709GammaAsSrgb
=
694 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
695 const bool rec2020GammaAsRec709
=
696 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
698 auto cspaceDesc
= color::ColorspaceDesc
{};
700 case gfx::ColorSpace2::Display
:
701 return; // No color management needed!
702 case gfx::ColorSpace2::SRGB
:
703 cspaceDesc
.chrom
= color::Chromaticities::Srgb();
704 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
707 case gfx::ColorSpace2::DISPLAY_P3
:
708 cspaceDesc
.chrom
= color::Chromaticities::DisplayP3();
709 cspaceDesc
.tf
= color::PiecewiseGammaDesc::DisplayP3();
712 case gfx::ColorSpace2::BT601_525
:
713 cspaceDesc
.chrom
= color::Chromaticities::Rec601_525_Ntsc();
714 if (rec709GammaAsSrgb
) {
715 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
717 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
721 case gfx::ColorSpace2::BT709
:
722 cspaceDesc
.chrom
= color::Chromaticities::Rec709();
723 if (rec709GammaAsSrgb
) {
724 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
726 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
730 case gfx::ColorSpace2::BT2020
:
731 cspaceDesc
.chrom
= color::Chromaticities::Rec2020();
732 if (rec2020GammaAsRec709
&& rec709GammaAsSrgb
) {
733 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Srgb();
734 } else if (rec2020GammaAsRec709
) {
735 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec709();
737 // Just Rec709 with slightly more precision.
738 cspaceDesc
.tf
= color::PiecewiseGammaDesc::Rec2020_12bit();
743 const auto cprofileIn
= color::ColorProfileDesc::From(cspaceDesc
);
744 auto cprofileOut
= mDCLayerTree
->OutputColorProfile();
745 bool pretendSrgb
= true;
747 cprofileOut
= color::ColorProfileDesc::From({
748 color::Chromaticities::Srgb(),
749 color::PiecewiseGammaDesc::Srgb(),
752 const auto conversion
= color::ColorProfileConversionDesc::From({
759 auto chain
= ColorManagementChain::From(*dcomp3
, conversion
);
760 mCManageChain
= Some(chain
);
762 surfaceVisual
->SetEffect(mCManageChain
->last
.get());
765 return mSurface
.get();
768 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix
& aTransform
) {
769 MOZ_ASSERT(mSurface
);
770 if (auto* surface
= mSurface
->AsDCSurfaceVideo()) {
771 if (surface
->CalculateSwapChainSize(aTransform
)) {
772 surface
->PresentVideo();
774 } else if (auto* surface
= mSurface
->AsDCSurfaceHandle()) {
775 surface
->PresentSurfaceHandle();
779 template <typename T
>
780 static inline D2D1_RECT_F
D2DRect(const T
& aRect
) {
781 return D2D1::RectF(aRect
.X(), aRect
.Y(), aRect
.XMost(), aRect
.YMost());
784 static inline D2D1_MATRIX_3X2_F
D2DMatrix(const gfx::Matrix
& aTransform
) {
785 return D2D1::Matrix3x2F(aTransform
._11
, aTransform
._12
, aTransform
._21
,
786 aTransform
._22
, aTransform
._31
, aTransform
._32
);
789 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId
,
790 const wr::CompositorSurfaceTransform
& aTransform
,
791 wr::DeviceIntRect aClipRect
,
792 wr::ImageRendering aImageRendering
) {
793 auto it
= mDCSurfaces
.find(aId
);
794 MOZ_RELEASE_ASSERT(it
!= mDCSurfaces
.end());
795 const auto surface
= it
->second
.get();
796 const auto visual
= surface
->GetVisual();
798 wr::DeviceIntPoint virtualOffset
= surface
->GetVirtualOffset();
800 float sx
= aTransform
.scale
.x
;
801 float sy
= aTransform
.scale
.y
;
802 float tx
= aTransform
.offset
.x
;
803 float ty
= aTransform
.offset
.y
;
804 gfx::Matrix
transform(sx
, 0.0, 0.0, sy
, tx
, ty
);
806 surface
->PresentExternalSurface(transform
);
808 transform
.PreTranslate(-virtualOffset
.x
, -virtualOffset
.y
);
810 // The DirectComposition API applies clipping *before* any
811 // transforms/offset, whereas we want the clip applied after. Right now, we
812 // only support rectilinear transforms, and then we transform our clip into
813 // pre-transform coordinate space for it to be applied there.
814 // DirectComposition does have an option for pre-transform clipping, if you
815 // create an explicit IDCompositionEffectGroup object and set a 3D transform
816 // on that. I suspect that will perform worse though, so we should only do
817 // that for complex transforms (which are never provided right now).
818 MOZ_ASSERT(transform
.IsRectilinear());
819 gfx::Rect clip
= transform
.Inverse().TransformBounds(gfx::Rect(
820 aClipRect
.min
.x
, aClipRect
.min
.y
, aClipRect
.width(), aClipRect
.height()));
821 // Set the clip rect - converting from world space to the pre-offset space
822 // that DC requires for rectangle clips.
823 visual
->SetClip(D2DRect(clip
));
825 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
826 // the D3D API level (unless we QI to IDCompositionVisual3, which might
827 // not be available?).
828 // Should we assert here, or restrict at the WR API level.
829 visual
->SetTransform(D2DMatrix(transform
));
831 if (aImageRendering
== wr::ImageRendering::Auto
) {
832 visual
->SetBitmapInterpolationMode(
833 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR
);
835 visual
->SetBitmapInterpolationMode(
836 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
);
839 mCurrentLayers
.push_back(aId
);
842 GLuint
DCLayerTree::GetOrCreateFbo(int aWidth
, int aHeight
) {
843 const auto gl
= GetGLContext();
846 // Check if we have a cached FBO with matching dimensions
847 for (auto it
= mFrameBuffers
.begin(); it
!= mFrameBuffers
.end(); ++it
) {
848 if (it
->width
== aWidth
&& it
->height
== aHeight
) {
850 it
->lastFrameUsed
= mCurrentFrame
;
855 // If not, create a new FBO with attached depth buffer
857 // Create the depth buffer
859 gl
->fGenRenderbuffers(1, &depthRboId
);
860 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, depthRboId
);
861 gl
->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, LOCAL_GL_DEPTH_COMPONENT24
,
864 // Create the framebuffer and attach the depth buffer to it
865 gl
->fGenFramebuffers(1, &fboId
);
866 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
867 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
868 LOCAL_GL_DEPTH_ATTACHMENT
,
869 LOCAL_GL_RENDERBUFFER
, depthRboId
);
871 // Store this in the cache for future calls.
872 // TODO(gw): Maybe we should periodically scan this list and remove old
874 // haven't been used for some time?
875 DCLayerTree::CachedFrameBuffer frame_buffer_info
;
876 frame_buffer_info
.width
= aWidth
;
877 frame_buffer_info
.height
= aHeight
;
878 frame_buffer_info
.fboId
= fboId
;
879 frame_buffer_info
.depthRboId
= depthRboId
;
880 frame_buffer_info
.lastFrameUsed
= mCurrentFrame
;
881 mFrameBuffers
.AppendElement(frame_buffer_info
);
887 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize
& aInputSize
,
888 const gfx::IntSize
& aOutputSize
) {
891 if (!mVideoDevice
|| !mVideoContext
) {
895 if (mVideoProcessor
&& (aInputSize
<= mVideoInputSize
) &&
896 (aOutputSize
<= mVideoOutputSize
)) {
900 mVideoProcessor
= nullptr;
901 mVideoProcessorEnumerator
= nullptr;
903 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc
= {};
904 desc
.InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE
;
905 desc
.InputFrameRate
.Numerator
= 60;
906 desc
.InputFrameRate
.Denominator
= 1;
907 desc
.InputWidth
= aInputSize
.width
;
908 desc
.InputHeight
= aInputSize
.height
;
909 desc
.OutputFrameRate
.Numerator
= 60;
910 desc
.OutputFrameRate
.Denominator
= 1;
911 desc
.OutputWidth
= aOutputSize
.width
;
912 desc
.OutputHeight
= aOutputSize
.height
;
913 desc
.Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
;
915 hr
= mVideoDevice
->CreateVideoProcessorEnumerator(
916 &desc
, getter_AddRefs(mVideoProcessorEnumerator
));
918 gfxCriticalNote
<< "Failed to create VideoProcessorEnumerator: "
923 hr
= mVideoDevice
->CreateVideoProcessor(mVideoProcessorEnumerator
, 0,
924 getter_AddRefs(mVideoProcessor
));
926 mVideoProcessor
= nullptr;
927 mVideoProcessorEnumerator
= nullptr;
928 gfxCriticalNote
<< "Failed to create VideoProcessor: " << gfx::hexa(hr
);
932 // Reduce power cosumption
933 // By default, the driver might perform certain processing tasks
935 mVideoContext
->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor
, 0,
938 mVideoInputSize
= aInputSize
;
939 mVideoOutputSize
= aOutputSize
;
944 bool DCLayerTree::SupportsHardwareOverlays() {
945 return sGpuOverlayInfo
->mSupportsHardwareOverlays
;
948 bool DCLayerTree::SupportsSwapChainTearing() {
949 RefPtr
<ID3D11Device
> device
= mDevice
;
950 static const bool supported
= [device
] {
951 RefPtr
<IDXGIDevice
> dxgiDevice
;
952 RefPtr
<IDXGIAdapter
> adapter
;
953 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
954 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
956 RefPtr
<IDXGIFactory5
> dxgiFactory
;
957 HRESULT hr
= adapter
->GetParent(
958 IID_PPV_ARGS((IDXGIFactory5
**)getter_AddRefs(dxgiFactory
)));
963 BOOL presentAllowTearing
= FALSE
;
964 hr
= dxgiFactory
->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING
,
965 &presentAllowTearing
,
966 sizeof(presentAllowTearing
));
971 if (auto* gpuParent
= gfx::GPUParent::GetSingleton()) {
972 gpuParent
->NotifySwapChainInfo(
973 layers::SwapChainInfo(!!presentAllowTearing
));
974 } else if (XRE_IsParentProcess()) {
975 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
977 return !!presentAllowTearing
;
982 DXGI_FORMAT
DCLayerTree::GetOverlayFormatForSDR() {
983 return sGpuOverlayInfo
->mOverlayFormatUsed
;
986 static layers::OverlaySupportType
FlagsToOverlaySupportType(
987 UINT aFlags
, bool aSoftwareOverlaySupported
) {
988 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_SCALING
) {
989 return layers::OverlaySupportType::Scaling
;
991 if (aFlags
& DXGI_OVERLAY_SUPPORT_FLAG_DIRECT
) {
992 return layers::OverlaySupportType::Direct
;
994 if (aSoftwareOverlaySupported
) {
995 return layers::OverlaySupportType::Software
;
997 return layers::OverlaySupportType::None
;
1000 layers::OverlayInfo
DCLayerTree::GetOverlayInfo() {
1001 layers::OverlayInfo info
;
1003 info
.mSupportsOverlays
= sGpuOverlayInfo
->mSupportsHardwareOverlays
;
1005 FlagsToOverlaySupportType(sGpuOverlayInfo
->mNv12OverlaySupportFlags
,
1006 /* aSoftwareOverlaySupported */ false);
1008 FlagsToOverlaySupportType(sGpuOverlayInfo
->mYuy2OverlaySupportFlags
,
1009 /* aSoftwareOverlaySupported */ false);
1010 info
.mBgra8Overlay
=
1011 FlagsToOverlaySupportType(sGpuOverlayInfo
->mBgra8OverlaySupportFlags
,
1012 /* aSoftwareOverlaySupported */ true);
1013 info
.mRgb10a2Overlay
=
1014 FlagsToOverlaySupportType(sGpuOverlayInfo
->mRgb10a2OverlaySupportFlags
,
1015 /* aSoftwareOverlaySupported */ false);
1020 void DCLayerTree::SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes
) {
1021 mUsedOverlayTypesInFrame
|= aTypes
;
1024 DCSurface::DCSurface(wr::DeviceIntSize aTileSize
,
1025 wr::DeviceIntPoint aVirtualOffset
, bool aIsVirtualSurface
,
1026 bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1027 : mIsVirtualSurface(aIsVirtualSurface
),
1028 mDCLayerTree(aDCLayerTree
),
1029 mTileSize(aTileSize
),
1030 mIsOpaque(aIsOpaque
),
1031 mAllocatedRectDirty(true),
1032 mVirtualOffset(aVirtualOffset
) {}
1034 DCSurface::~DCSurface() {}
1036 bool DCSurface::Initialize() {
1037 // Create a visual for tiles to attach to, whether virtual or not.
1039 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1040 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1042 gfxCriticalNote
<< "Failed to create DCompositionVisual: " << gfx::hexa(hr
);
1046 // If virtual surface is enabled, create and attach to visual, in this case
1047 // the tiles won't own visuals or surfaces.
1048 if (mIsVirtualSurface
) {
1049 DXGI_ALPHA_MODE alpha_mode
=
1050 mIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1052 hr
= dCompDevice
->CreateVirtualSurface(
1053 VIRTUAL_SURFACE_SIZE
, VIRTUAL_SURFACE_SIZE
, DXGI_FORMAT_R8G8B8A8_UNORM
,
1054 alpha_mode
, getter_AddRefs(mVirtualSurface
));
1055 MOZ_ASSERT(SUCCEEDED(hr
));
1057 // Bind the surface memory to this visual
1058 hr
= mVisual
->SetContent(mVirtualSurface
);
1059 MOZ_ASSERT(SUCCEEDED(hr
));
1065 void DCSurface::CreateTile(int32_t aX
, int32_t aY
) {
1066 TileKey
key(aX
, aY
);
1067 MOZ_RELEASE_ASSERT(mDCTiles
.find(key
) == mDCTiles
.end());
1069 auto tile
= MakeUnique
<DCTile
>(mDCLayerTree
);
1070 if (!tile
->Initialize(aX
, aY
, mTileSize
, mIsVirtualSurface
, mIsOpaque
,
1072 gfxCriticalNote
<< "Failed to initialize DCTile: " << aX
<< aY
;
1076 if (mIsVirtualSurface
) {
1077 mAllocatedRectDirty
= true;
1079 mVisual
->AddVisual(tile
->GetVisual(), false, nullptr);
1082 mDCTiles
[key
] = std::move(tile
);
1085 void DCSurface::DestroyTile(int32_t aX
, int32_t aY
) {
1086 TileKey
key(aX
, aY
);
1087 if (mIsVirtualSurface
) {
1088 mAllocatedRectDirty
= true;
1090 auto tile
= GetTile(aX
, aY
);
1091 mVisual
->RemoveVisual(tile
->GetVisual());
1093 mDCTiles
.erase(key
);
1096 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty
= true; }
1098 void DCSurface::UpdateAllocatedRect() {
1099 if (mAllocatedRectDirty
) {
1100 if (mVirtualSurface
) {
1101 // The virtual surface may have holes in it (for example, an empty tile
1102 // that has no primitives). Instead of trimming to a single bounding
1103 // rect, supply the rect of each valid tile to handle this case.
1104 std::vector
<RECT
> validRects
;
1106 for (auto it
= mDCTiles
.begin(); it
!= mDCTiles
.end(); ++it
) {
1107 auto tile
= GetTile(it
->first
.mX
, it
->first
.mY
);
1110 rect
.left
= (LONG
)(mVirtualOffset
.x
+ it
->first
.mX
* mTileSize
.width
+
1111 tile
->mValidRect
.x
);
1112 rect
.top
= (LONG
)(mVirtualOffset
.y
+ it
->first
.mY
* mTileSize
.height
+
1113 tile
->mValidRect
.y
);
1114 rect
.right
= rect
.left
+ tile
->mValidRect
.width
;
1115 rect
.bottom
= rect
.top
+ tile
->mValidRect
.height
;
1117 validRects
.push_back(rect
);
1120 mVirtualSurface
->Trim(validRects
.data(), validRects
.size());
1122 // When not using a virtual surface, we still want to reset this
1123 mAllocatedRectDirty
= false;
1127 DCTile
* DCSurface::GetTile(int32_t aX
, int32_t aY
) const {
1128 TileKey
key(aX
, aY
);
1129 auto tile_it
= mDCTiles
.find(key
);
1130 MOZ_RELEASE_ASSERT(tile_it
!= mDCTiles
.end());
1131 return tile_it
->second
.get();
1134 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1135 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1138 DCSurfaceVideo::~DCSurfaceVideo() {
1139 ReleaseDecodeSwapChainResources();
1140 MOZ_ASSERT(!mSwapChainSurfaceHandle
);
1143 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat
) {
1144 if (aFormat
== DXGI_FORMAT_NV12
|| aFormat
== DXGI_FORMAT_YUY2
) {
1150 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1151 RenderTextureHost
* texture
=
1152 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1153 MOZ_RELEASE_ASSERT(texture
);
1155 if (mPrevTexture
== texture
) {
1159 // XXX if software decoded video frame format is nv12, it could be used as
1161 if (!texture
|| !texture
->AsRenderDXGITextureHost() ||
1162 texture
->GetFormat() != gfx::SurfaceFormat::NV12
) {
1163 gfxCriticalNote
<< "Unsupported RenderTexture for overlay: "
1164 << gfx::hexa(texture
);
1168 mRenderTextureHost
= texture
;
1171 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix
& aTransform
) {
1172 if (!mRenderTextureHost
) {
1173 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1177 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1178 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1179 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1180 mDCLayerTree
->SetUsedOverlayTypeInFrame(overlayType
);
1182 mVideoSize
= mRenderTextureHost
->AsRenderDXGITextureHost()->GetSize(0);
1184 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1185 // DCSurfaceVideo::PresentVideo() needs to be called.
1186 bool needsToPresent
= mPrevTexture
!= mRenderTextureHost
;
1187 gfx::IntSize swapChainSize
= mVideoSize
;
1188 gfx::Matrix transform
= aTransform
;
1189 const bool isDRM
= mRenderTextureHost
->IsFromDRMSource();
1191 // When video is rendered to axis aligned integer rectangle, video scaling
1192 // could be done by VideoProcessor
1193 bool scaleVideoAtVideoProcessor
= false;
1194 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1195 aTransform
.PreservesAxisAlignedRectangles()) {
1196 gfx::Size scaledSize
= gfx::Size(mVideoSize
) * aTransform
.ScaleFactors();
1197 gfx::IntSize
size(int32_t(std::round(scaledSize
.width
)),
1198 int32_t(std::round(scaledSize
.height
)));
1199 if (gfx::FuzzyEqual(scaledSize
.width
, size
.width
, 0.1f
) &&
1200 gfx::FuzzyEqual(scaledSize
.height
, size
.height
, 0.1f
)) {
1201 scaleVideoAtVideoProcessor
= true;
1202 swapChainSize
= size
;
1206 if (scaleVideoAtVideoProcessor
) {
1207 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1208 // subsampled formats like NV12 must have an even width and height.
1209 if (swapChainSize
.width
% 2 == 1) {
1210 swapChainSize
.width
+= 1;
1212 if (swapChainSize
.height
% 2 == 1) {
1213 swapChainSize
.height
+= 1;
1215 transform
= gfx::Matrix::Translation(aTransform
.GetTranslation());
1218 if (!mVideoSwapChain
|| mSwapChainSize
!= swapChainSize
|| mIsDRM
!= isDRM
) {
1219 needsToPresent
= true;
1220 ReleaseDecodeSwapChainResources();
1221 // Update mSwapChainSize before creating SwapChain
1222 mSwapChainSize
= swapChainSize
;
1225 auto swapChainFormat
= GetSwapChainFormat();
1226 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1227 if (useYUVSwapChain
) {
1228 // Tries to create YUV SwapChain
1229 CreateVideoSwapChain();
1230 if (!mVideoSwapChain
) {
1231 mFailedYuvSwapChain
= true;
1232 ReleaseDecodeSwapChainResources();
1234 gfxCriticalNote
<< "Fallback to RGB SwapChain";
1237 // Tries to create RGB SwapChain
1238 if (!mVideoSwapChain
) {
1239 CreateVideoSwapChain();
1243 aTransform
= transform
;
1245 return needsToPresent
;
1248 void DCSurfaceVideo::PresentVideo() {
1249 if (!mRenderTextureHost
) {
1253 if (!mVideoSwapChain
) {
1254 gfxCriticalNote
<< "Failed to create VideoSwapChain";
1255 RenderThread::Get()->NotifyWebRenderError(
1256 wr::WebRenderError::VIDEO_OVERLAY
);
1260 mVisual
->SetContent(mVideoSwapChain
);
1262 if (!CallVideoProcessorBlt()) {
1263 auto swapChainFormat
= GetSwapChainFormat();
1264 bool useYUVSwapChain
= IsYUVSwapChainFormat(swapChainFormat
);
1265 if (useYUVSwapChain
) {
1266 mFailedYuvSwapChain
= true;
1267 ReleaseDecodeSwapChainResources();
1270 RenderThread::Get()->NotifyWebRenderError(
1271 wr::WebRenderError::VIDEO_OVERLAY
);
1275 auto start
= TimeStamp::Now();
1276 HRESULT hr
= mVideoSwapChain
->Present(0, 0);
1277 auto end
= TimeStamp::Now();
1279 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
1280 gfxCriticalNoteOnce
<< "video Present failed: " << gfx::hexa(hr
);
1283 mPrevTexture
= mRenderTextureHost
;
1285 // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops
1288 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
1292 const auto maxPresentWaitDurationMs
= 2;
1293 const auto maxSlowPresentCount
= 5;
1294 const auto presentDurationMs
=
1295 static_cast<uint32_t>((end
- start
).ToMilliseconds());
1296 const auto overlayType
= mRenderTextureHost
->IsSoftwareDecodedVideo()
1297 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1298 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO
;
1300 nsPrintfCString
marker("PresentWait overlay %u %ums ", (uint8_t)overlayType
,
1302 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS
, {}, marker
);
1304 if (presentDurationMs
> maxPresentWaitDurationMs
) {
1305 mSlowPresentCount
++;
1307 mSlowPresentCount
= 0;
1310 if (mSlowPresentCount
<= maxSlowPresentCount
) {
1314 if (overlayType
== DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
) {
1315 gfxCriticalNoteOnce
<< "Sw video swapchain present is slow";
1316 RenderThread::Get()->NotifyWebRenderError(
1317 wr::WebRenderError::VIDEO_SW_OVERLAY
);
1319 gfxCriticalNoteOnce
<< "Hw video swapchain present is slow";
1320 RenderThread::Get()->NotifyWebRenderError(
1321 wr::WebRenderError::VIDEO_HW_OVERLAY
);
1325 DXGI_FORMAT
DCSurfaceVideo::GetSwapChainFormat() {
1326 if (mFailedYuvSwapChain
|| !mDCLayerTree
->SupportsHardwareOverlays()) {
1327 return DXGI_FORMAT_B8G8R8A8_UNORM
;
1329 return mDCLayerTree
->GetOverlayFormatForSDR();
1332 bool DCSurfaceVideo::CreateVideoSwapChain() {
1333 MOZ_ASSERT(mRenderTextureHost
);
1335 const auto device
= mDCLayerTree
->GetDevice();
1337 RefPtr
<IDXGIDevice
> dxgiDevice
;
1338 device
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1340 RefPtr
<IDXGIFactoryMedia
> dxgiFactoryMedia
;
1342 RefPtr
<IDXGIAdapter
> adapter
;
1343 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1345 IID_PPV_ARGS((IDXGIFactoryMedia
**)getter_AddRefs(dxgiFactoryMedia
)));
1348 mSwapChainSurfaceHandle
= gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1349 if (!mSwapChainSurfaceHandle
) {
1350 gfxCriticalNote
<< "Failed to create DCompSurfaceHandle";
1354 auto swapChainFormat
= GetSwapChainFormat();
1355 bool useTripleBuffering
=
1356 StaticPrefs::gfx_webrender_dcomp_video_force_triple_buffering();
1358 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1359 desc
.Width
= mSwapChainSize
.width
;
1360 desc
.Height
= mSwapChainSize
.height
;
1361 desc
.Format
= swapChainFormat
;
1362 desc
.Stereo
= FALSE
;
1363 desc
.SampleDesc
.Count
= 1;
1364 desc
.BufferCount
= useTripleBuffering
? 3 : 2;
1365 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1366 desc
.Scaling
= DXGI_SCALING_STRETCH
;
1367 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1368 desc
.Flags
= DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO
;
1369 if (IsYUVSwapChainFormat(swapChainFormat
)) {
1370 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO
;
1373 desc
.Flags
|= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY
;
1375 desc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
1378 hr
= dxgiFactoryMedia
->CreateSwapChainForCompositionSurfaceHandle(
1379 device
, mSwapChainSurfaceHandle
, &desc
, nullptr,
1380 getter_AddRefs(mVideoSwapChain
));
1383 gfxCriticalNote
<< "Failed to create video SwapChain: " << gfx::hexa(hr
)
1384 << " " << mSwapChainSize
;
1388 mSwapChainFormat
= swapChainFormat
;
1392 // TODO: Replace with YUVRangedColorSpace
1393 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1394 const gfx::YUVColorSpace aYUVColorSpace
,
1395 const gfx::ColorRange aColorRange
) {
1396 if (aYUVColorSpace
== gfx::YUVColorSpace::BT601
) {
1397 if (aColorRange
== gfx::ColorRange::FULL
) {
1398 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601
);
1400 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601
);
1402 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT709
) {
1403 if (aColorRange
== gfx::ColorRange::FULL
) {
1404 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709
);
1406 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709
);
1408 } else if (aYUVColorSpace
== gfx::YUVColorSpace::BT2020
) {
1409 if (aColorRange
== gfx::ColorRange::FULL
) {
1410 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1412 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020
);
1414 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020
);
1421 static Maybe
<DXGI_COLOR_SPACE_TYPE
> GetSourceDXGIColorSpace(
1422 const gfx::YUVRangedColorSpace aYUVColorSpace
) {
1423 const auto info
= FromYUVRangedColorSpace(aYUVColorSpace
);
1424 return GetSourceDXGIColorSpace(info
.space
, info
.range
);
1427 static void SetNvidiaVideoSuperRes(ID3D11VideoContext
* videoContext
,
1428 ID3D11VideoProcessor
* videoProcessor
,
1430 LOG("SetNvidiaVideoSuperRes() enabled=%d", enabled
);
1432 // Undocumented NVIDIA driver constants
1433 constexpr GUID nvGUID
= {0xD43CE1B3,
1436 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
1438 constexpr UINT nvExtensionVersion
= 0x1;
1439 constexpr UINT nvExtensionMethodSuperResolution
= 0x2;
1444 } streamExtensionInfo
= {nvExtensionVersion
, nvExtensionMethodSuperResolution
,
1448 hr
= videoContext
->VideoProcessorSetStreamExtension(
1449 videoProcessor
, 0, &nvGUID
, sizeof(streamExtensionInfo
),
1450 &streamExtensionInfo
);
1452 // Ignore errors as could be unsupported
1454 LOG("SetNvidiaVideoSuperRes() error: %lx", hr
);
1459 static UINT
GetVendorId(ID3D11VideoDevice
* const videoDevice
) {
1460 RefPtr
<IDXGIDevice
> dxgiDevice
;
1461 RefPtr
<IDXGIAdapter
> adapter
;
1462 videoDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
1463 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
1465 DXGI_ADAPTER_DESC adapterDesc
;
1466 adapter
->GetDesc(&adapterDesc
);
1468 return adapterDesc
.VendorId
;
1471 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1472 MOZ_ASSERT(mRenderTextureHost
);
1475 const auto videoDevice
= mDCLayerTree
->GetVideoDevice();
1476 const auto videoContext
= mDCLayerTree
->GetVideoContext();
1477 const auto texture
= mRenderTextureHost
->AsRenderDXGITextureHost();
1479 Maybe
<DXGI_COLOR_SPACE_TYPE
> sourceColorSpace
=
1480 GetSourceDXGIColorSpace(texture
->GetYUVColorSpace());
1481 if (sourceColorSpace
.isNothing()) {
1482 gfxCriticalNote
<< "Unsupported color space";
1486 RefPtr
<ID3D11Texture2D
> texture2D
= texture
->GetD3D11Texture2DWithGL();
1488 gfxCriticalNote
<< "Failed to get D3D11Texture2D";
1492 if (!mVideoSwapChain
) {
1496 if (!mDCLayerTree
->EnsureVideoProcessor(mVideoSize
, mSwapChainSize
)) {
1497 gfxCriticalNote
<< "EnsureVideoProcessor Failed";
1501 RefPtr
<IDXGISwapChain3
> swapChain3
;
1502 mVideoSwapChain
->QueryInterface(
1503 (IDXGISwapChain3
**)getter_AddRefs(swapChain3
));
1505 gfxCriticalNote
<< "Failed to get IDXGISwapChain3";
1509 RefPtr
<ID3D11VideoContext1
> videoContext1
;
1510 videoContext
->QueryInterface(
1511 (ID3D11VideoContext1
**)getter_AddRefs(videoContext1
));
1512 if (!videoContext1
) {
1513 gfxCriticalNote
<< "Failed to get ID3D11VideoContext1";
1517 const auto videoProcessor
= mDCLayerTree
->GetVideoProcessor();
1518 const auto videoProcessorEnumerator
=
1519 mDCLayerTree
->GetVideoProcessorEnumerator();
1521 DXGI_COLOR_SPACE_TYPE inputColorSpace
= sourceColorSpace
.ref();
1522 videoContext1
->VideoProcessorSetStreamColorSpace1(videoProcessor
, 0,
1525 DXGI_COLOR_SPACE_TYPE outputColorSpace
=
1526 IsYUVSwapChainFormat(mSwapChainFormat
)
1528 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1529 hr
= swapChain3
->SetColorSpace1(outputColorSpace
);
1531 gfxCriticalNoteOnce
<< "SetColorSpace1 failed: " << gfx::hexa(hr
);
1532 RenderThread::Get()->NotifyWebRenderError(
1533 wr::WebRenderError::VIDEO_OVERLAY
);
1536 videoContext1
->VideoProcessorSetOutputColorSpace1(videoProcessor
,
1539 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc
= {};
1540 inputDesc
.ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
;
1541 inputDesc
.Texture2D
.ArraySlice
= texture
->ArrayIndex();
1543 RefPtr
<ID3D11VideoProcessorInputView
> inputView
;
1544 hr
= videoDevice
->CreateVideoProcessorInputView(
1545 texture2D
, videoProcessorEnumerator
, &inputDesc
,
1546 getter_AddRefs(inputView
));
1548 gfxCriticalNote
<< "ID3D11VideoProcessorInputView creation failed: "
1553 D3D11_VIDEO_PROCESSOR_STREAM stream
= {};
1554 stream
.Enable
= true;
1555 stream
.OutputIndex
= 0;
1556 stream
.InputFrameOrField
= 0;
1557 stream
.PastFrames
= 0;
1558 stream
.FutureFrames
= 0;
1559 stream
.pInputSurface
= inputView
.get();
1564 destRect
.right
= mSwapChainSize
.width
;
1565 destRect
.bottom
= mSwapChainSize
.height
;
1567 videoContext
->VideoProcessorSetOutputTargetRect(videoProcessor
, TRUE
,
1569 videoContext
->VideoProcessorSetStreamDestRect(videoProcessor
, 0, TRUE
,
1572 sourceRect
.left
= 0;
1574 sourceRect
.right
= mVideoSize
.width
;
1575 sourceRect
.bottom
= mVideoSize
.height
;
1576 videoContext
->VideoProcessorSetStreamSourceRect(videoProcessor
, 0, TRUE
,
1580 RefPtr
<ID3D11Texture2D
> backBuf
;
1581 mVideoSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
1582 (void**)getter_AddRefs(backBuf
));
1584 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc
= {};
1585 outputDesc
.ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2D
;
1586 outputDesc
.Texture2D
.MipSlice
= 0;
1588 hr
= videoDevice
->CreateVideoProcessorOutputView(
1589 backBuf
, videoProcessorEnumerator
, &outputDesc
,
1590 getter_AddRefs(mOutputView
));
1592 gfxCriticalNote
<< "ID3D11VideoProcessorOutputView creation failed: "
1598 const UINT vendorId
= GetVendorId(videoDevice
);
1599 if (vendorId
== 0x10DE &&
1600 StaticPrefs::gfx_webrender_super_resolution_nvidia_AtStartup()) {
1601 SetNvidiaVideoSuperRes(videoContext
, videoProcessor
, true);
1604 hr
= videoContext
->VideoProcessorBlt(videoProcessor
, mOutputView
, 0, 1,
1607 gfxCriticalNote
<< "VideoProcessorBlt failed: " << gfx::hexa(hr
);
1614 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1615 mOutputView
= nullptr;
1616 mVideoSwapChain
= nullptr;
1617 mDecodeSwapChain
= nullptr;
1618 mDecodeResource
= nullptr;
1619 if (mSwapChainSurfaceHandle
) {
1620 ::CloseHandle(mSwapChainSurfaceHandle
);
1621 mSwapChainSurfaceHandle
= 0;
1625 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque
, DCLayerTree
* aDCLayerTree
)
1626 : DCSurface(wr::DeviceIntSize
{}, wr::DeviceIntPoint
{}, false, aIsOpaque
,
1629 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage
) {
1630 RenderTextureHost
* texture
=
1631 RenderThread::Get()->GetRenderTexture(aExternalImage
);
1632 RenderDcompSurfaceTextureHost
* renderTexture
=
1633 texture
? texture
->AsRenderDcompSurfaceTextureHost() : nullptr;
1634 if (!renderTexture
) {
1635 gfxCriticalNote
<< "Unsupported RenderTexture for DCSurfaceHandle: "
1636 << gfx::hexa(texture
);
1640 const auto handle
= renderTexture
->GetDcompSurfaceHandle();
1641 if (GetSurfaceHandle() == handle
) {
1645 LOG_H("AttachExternalImage, ext-image=%" PRIu64
", texture=%p, handle=%p",
1646 wr::AsUint64(aExternalImage
), renderTexture
, handle
);
1647 mDcompTextureHost
= renderTexture
;
1650 HANDLE
DCSurfaceHandle::GetSurfaceHandle() const {
1651 if (mDcompTextureHost
) {
1652 return mDcompTextureHost
->GetDcompSurfaceHandle();
1657 IDCompositionSurface
* DCSurfaceHandle::EnsureSurface() {
1658 if (auto* surface
= mDcompTextureHost
->GetSurface()) {
1662 // Texture host hasn't created the surface yet, ask it to create a new one.
1663 RefPtr
<IDCompositionDevice
> device
;
1664 HRESULT hr
= mDCLayerTree
->GetCompositionDevice()->QueryInterface(
1665 (IDCompositionDevice
**)getter_AddRefs(device
));
1668 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1673 return mDcompTextureHost
->CreateSurfaceFromDevice(device
);
1676 void DCSurfaceHandle::PresentSurfaceHandle() {
1677 LOG_H("PresentSurfaceHandle");
1678 if (IDCompositionSurface
* surface
= EnsureSurface()) {
1679 LOG_H("Set surface %p to visual", surface
);
1680 mVisual
->SetContent(surface
);
1682 mVisual
->SetContent(nullptr);
1686 DCTile::DCTile(DCLayerTree
* aDCLayerTree
) : mDCLayerTree(aDCLayerTree
) {}
1688 DCTile::~DCTile() {}
1690 bool DCTile::Initialize(int aX
, int aY
, wr::DeviceIntSize aSize
,
1691 bool aIsVirtualSurface
, bool aIsOpaque
,
1692 RefPtr
<IDCompositionVisual2
> mSurfaceVisual
) {
1693 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
1698 mIsOpaque
= aIsOpaque
;
1699 mIsVirtualSurface
= aIsVirtualSurface
;
1700 mNeedsFullDraw
= !aIsVirtualSurface
;
1702 if (aIsVirtualSurface
) {
1703 // Initially, the entire tile is considered valid, unless it is set by
1704 // the SetTileProperties method.
1707 mValidRect
.width
= aSize
.width
;
1708 mValidRect
.height
= aSize
.height
;
1711 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1712 // Create the visual and put it in the tree under the surface visual
1713 hr
= dCompDevice
->CreateVisual(getter_AddRefs(mVisual
));
1715 gfxCriticalNote
<< "Failed to CreateVisual for DCTile: " << gfx::hexa(hr
);
1718 mSurfaceVisual
->AddVisual(mVisual
, false, nullptr);
1719 // Position the tile relative to the surface visual
1720 mVisual
->SetOffsetX(aX
* aSize
.width
);
1721 mVisual
->SetOffsetY(aY
* aSize
.height
);
1722 // Clip the visual so it doesn't show anything until we update it
1723 D2D_RECT_F clip
= {0, 0, 0, 0};
1724 mVisual
->SetClip(clip
);
1725 // Create the underlying pixel buffer.
1726 mCompositionSurface
= CreateCompositionSurface(aSize
, aIsOpaque
);
1727 if (!mCompositionSurface
) {
1730 hr
= mVisual
->SetContent(mCompositionSurface
);
1732 gfxCriticalNote
<< "Failed to SetContent for DCTile: " << gfx::hexa(hr
);
1740 RefPtr
<IDCompositionSurface
> DCTile::CreateCompositionSurface(
1741 wr::DeviceIntSize aSize
, bool aIsOpaque
) {
1743 const auto dCompDevice
= mDCLayerTree
->GetCompositionDevice();
1744 const auto alphaMode
=
1745 aIsOpaque
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED
;
1746 RefPtr
<IDCompositionSurface
> compositionSurface
;
1748 hr
= dCompDevice
->CreateSurface(aSize
.width
, aSize
.height
,
1749 DXGI_FORMAT_R8G8B8A8_UNORM
, alphaMode
,
1750 getter_AddRefs(compositionSurface
));
1752 gfxCriticalNote
<< "Failed to CreateSurface for DCTile: " << gfx::hexa(hr
);
1755 return compositionSurface
;
1758 RefPtr
<IDCompositionSurface
> DCTile::Bind(wr::DeviceIntRect aValidRect
) {
1759 if (mVisual
!= nullptr) {
1760 // Tile owns a visual, set the size of the visual to match the portion we
1761 // want to be visible.
1762 D2D_RECT_F clip_rect
;
1763 clip_rect
.left
= aValidRect
.min
.x
;
1764 clip_rect
.top
= aValidRect
.min
.y
;
1765 clip_rect
.right
= aValidRect
.max
.x
;
1766 clip_rect
.bottom
= aValidRect
.max
.y
;
1767 mVisual
->SetClip(clip_rect
);
1769 return mCompositionSurface
;
1772 GLuint
DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1773 wr::DeviceIntRect aDirtyRect
, wr::DeviceIntPoint
* aOffset
,
1774 RefPtr
<IDCompositionSurface
> aCompositionSurface
,
1775 wr::DeviceIntPoint aSurfaceOffset
) {
1776 MOZ_ASSERT(aCompositionSurface
.get());
1779 const auto gl
= GetGLContext();
1780 RefPtr
<ID3D11Texture2D
> backBuf
;
1784 update_rect
.left
= aSurfaceOffset
.x
+ aDirtyRect
.min
.x
;
1785 update_rect
.top
= aSurfaceOffset
.y
+ aDirtyRect
.min
.y
;
1786 update_rect
.right
= aSurfaceOffset
.x
+ aDirtyRect
.max
.x
;
1787 update_rect
.bottom
= aSurfaceOffset
.y
+ aDirtyRect
.max
.y
;
1788 hr
= aCompositionSurface
->BeginDraw(&update_rect
, __uuidof(ID3D11Texture2D
),
1789 (void**)getter_AddRefs(backBuf
), &offset
);
1792 LayoutDeviceIntRect rect
= widget::WinUtils::ToIntRect(update_rect
);
1794 gfxCriticalNote
<< "DCompositionSurface::BeginDraw failed: "
1795 << gfx::hexa(hr
) << " " << rect
;
1796 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW
);
1800 // DC includes the origin of the dirty / update rect in the draw offset,
1801 // undo that here since WR expects it to be an absolute offset.
1802 offset
.x
-= aDirtyRect
.min
.x
;
1803 offset
.y
-= aDirtyRect
.min
.y
;
1805 D3D11_TEXTURE2D_DESC desc
;
1806 backBuf
->GetDesc(&desc
);
1808 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1809 const auto& egl
= gle
->mEgl
;
1811 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
1813 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
1814 const EGLint attribs
[] = {LOCAL_EGL_NONE
};
1815 mEGLImage
= egl
->fCreateImage(EGL_NO_CONTEXT
, LOCAL_EGL_D3D11_TEXTURE_ANGLE
,
1818 // Get the current FBO and RBO id, so we can restore them later
1819 GLint currentFboId
, currentRboId
;
1820 gl
->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING
, ¤tFboId
);
1821 gl
->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING
, ¤tRboId
);
1823 // Create a render buffer object that is backed by the EGL image.
1824 gl
->fGenRenderbuffers(1, &mColorRBO
);
1825 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, mColorRBO
);
1826 gl
->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER
, mEGLImage
);
1828 // Get or create an FBO for the specified dimensions
1829 GLuint fboId
= GetOrCreateFbo(desc
.Width
, desc
.Height
);
1831 // Attach the new renderbuffer to the FBO
1832 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, fboId
);
1833 gl
->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER
,
1834 LOCAL_GL_COLOR_ATTACHMENT0
,
1835 LOCAL_GL_RENDERBUFFER
, mColorRBO
);
1837 // Restore previous FBO and RBO bindings
1838 gl
->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER
, currentFboId
);
1839 gl
->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER
, currentRboId
);
1841 aOffset
->x
= offset
.x
;
1842 aOffset
->y
= offset
.y
;
1847 void DCLayerTree::DestroyEGLSurface() {
1848 const auto gl
= GetGLContext();
1851 gl
->fDeleteRenderbuffers(1, &mColorRBO
);
1856 const auto& gle
= gl::GLContextEGL::Cast(gl
);
1857 const auto& egl
= gle
->mEgl
;
1858 egl
->fDestroyImage(mEGLImage
);
1859 mEGLImage
= EGL_NO_IMAGE
;
1865 color::ColorProfileDesc
DCLayerTree::QueryOutputColorProfile() {
1866 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
1867 // but we do need gfxPlatform::GetCMSOutputProfile().
1868 // So we steal what we need through the window:
1869 const auto outputProfileData
=
1870 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
1872 const auto qcmsProfile
= qcms_profile_from_memory(
1873 outputProfileData
.Elements(), outputProfileData
.Length());
1874 const auto release
= MakeScopeExit([&]() {
1876 qcms_profile_release(qcmsProfile
);
1880 const bool print
= gfxEnv::MOZ_GL_SPEW();
1882 const auto ret
= [&]() {
1884 return color::ColorProfileDesc::From(*qcmsProfile
);
1888 "Missing or failed to load display color profile, defaulting to "
1891 const auto MISSING_PROFILE_DEFAULT_SPACE
= color::ColorspaceDesc
{
1892 color::Chromaticities::Srgb(),
1893 color::PiecewiseGammaDesc::Srgb(),
1895 return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE
);
1899 const auto gammaGuess
= color::GuessGamma(ret
.linearFromTf
.r
);
1901 "Display profile:\n"
1902 " Approx Gamma: %f\n"
1903 " XYZ-D65 Red : %f, %f, %f\n"
1904 " XYZ-D65 Green: %f, %f, %f\n"
1905 " XYZ-D65 Blue : %f, %f, %f\n",
1906 gammaGuess
, ret
.xyzd65FromLinearRgb
.at(0, 0),
1907 ret
.xyzd65FromLinearRgb
.at(0, 1), ret
.xyzd65FromLinearRgb
.at(0, 2),
1909 ret
.xyzd65FromLinearRgb
.at(1, 0), ret
.xyzd65FromLinearRgb
.at(1, 1),
1910 ret
.xyzd65FromLinearRgb
.at(1, 2),
1912 ret
.xyzd65FromLinearRgb
.at(2, 0), ret
.xyzd65FromLinearRgb
.at(2, 1),
1913 ret
.xyzd65FromLinearRgb
.at(2, 2));
1919 inline D2D1_MATRIX_5X4_F
to_D2D1_MATRIX_5X4_F(const color::mat4
& m
) {
1920 return D2D1_MATRIX_5X4_F
{{{
1944 ColorManagementChain
ColorManagementChain::From(
1945 IDCompositionDevice3
& dcomp
,
1946 const color::ColorProfileConversionDesc
& conv
) {
1947 auto ret
= ColorManagementChain
{};
1949 #if !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
1951 const auto Append
= [&](const RefPtr
<IDCompositionFilterEffect
>& afterLast
) {
1953 afterLast
->SetInput(0, ret
.last
, 0);
1955 ret
.last
= afterLast
;
1958 const auto MaybeAppendColorMatrix
= [&](const color::mat4
& m
) {
1959 RefPtr
<IDCompositionColorMatrixEffect
> e
;
1960 if (approx(m
, color::mat4::Identity())) return e
;
1961 dcomp
.CreateColorMatrixEffect(getter_AddRefs(e
));
1964 e
->SetMatrix(to_D2D1_MATRIX_5X4_F(m
));
1968 const auto MaybeAppendTableTransfer
= [&](const color::RgbTransferTables
& t
) {
1969 RefPtr
<IDCompositionTableTransferEffect
> e
;
1970 if (!t
.r
.size() && !t
.g
.size() && !t
.b
.size()) return e
;
1971 dcomp
.CreateTableTransferEffect(getter_AddRefs(e
));
1974 e
->SetRedTable(t
.r
.data(), t
.r
.size());
1975 e
->SetGreenTable(t
.g
.data(), t
.g
.size());
1976 e
->SetBlueTable(t
.b
.data(), t
.b
.size());
1981 ret
.srcRgbFromSrcYuv
= MaybeAppendColorMatrix(conv
.srcRgbFromSrcYuv
);
1982 ret
.srcLinearFromSrcTf
= MaybeAppendTableTransfer(conv
.srcLinearFromSrcTf
);
1983 ret
.dstLinearFromSrcLinear
=
1984 MaybeAppendColorMatrix(color::mat4(conv
.dstLinearFromSrcLinear
));
1985 ret
.dstTfFromDstLinear
= MaybeAppendTableTransfer(conv
.dstTfFromDstLinear
);
1987 #endif // !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
1992 ColorManagementChain::~ColorManagementChain() = default;
1995 } // namespace mozilla