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