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