Backed out changeset 39e6a7e77cfb (bug 1927808) for causing multiple failures. CLOSED...
[gecko.git] / gfx / webrender_bindings / DCLayerTree.cpp
blob756bd63e0fd6c190ec9f3737b24bd1dfa6019bd1
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 namespace mozilla {
41 namespace wr {
43 extern LazyLogModule gRenderThreadLog;
44 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
46 #define LOG_H(msg, ...) \
47 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
48 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
50 static UINT GetVendorId(ID3D11VideoDevice* const aVideoDevice) {
51 RefPtr<IDXGIDevice> dxgiDevice;
52 RefPtr<IDXGIAdapter> adapter;
53 aVideoDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
54 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
56 DXGI_ADAPTER_DESC adapterDesc;
57 adapter->GetDesc(&adapterDesc);
59 return adapterDesc.VendorId;
62 // Undocumented NVIDIA VSR data
63 struct NvidiaVSRGetData_v1 {
64 UINT vsrGPUisVSRCapable : 1; // 01/32, 1: GPU is VSR capable
65 UINT vsrOtherFieldsValid : 1; // 02/32, 1: Other status fields are valid
66 // remaining fields are valid if vsrOtherFieldsValid is set - requires
67 // previous execution of VPBlt with SetStreamExtension for VSR enabled.
68 UINT vsrEnabled : 1; // 03/32, 1: VSR is enabled
69 UINT vsrIsInUseForThisVP : 1; // 04/32, 1: VSR is in use by this Video
70 // Processor
71 UINT vsrLevel : 3; // 05-07/32, 0-4 current level
72 UINT vsrReserved : 21; // 32-07
75 static Result<NvidiaVSRGetData_v1, HRESULT> GetNvidiaVpSuperResolutionInfo(
76 ID3D11VideoContext* aVideoContext, ID3D11VideoProcessor* aVideoProcessor) {
77 MOZ_ASSERT(aVideoContext);
78 MOZ_ASSERT(aVideoProcessor);
80 // Undocumented NVIDIA driver constants
81 constexpr GUID nvGUID = {0xD43CE1B3,
82 0x1F4B,
83 0x48AC,
84 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
86 NvidiaVSRGetData_v1 data{};
87 HRESULT hr = aVideoContext->VideoProcessorGetStreamExtension(
88 aVideoProcessor, 0, &nvGUID, sizeof(data), &data);
90 if (FAILED(hr)) {
91 return Err(hr);
93 return data;
96 static void AddProfileMarkerForNvidiaVpSuperResolutionInfo(
97 ID3D11VideoContext* aVideoContext, ID3D11VideoProcessor* aVideoProcessor) {
98 MOZ_ASSERT(profiler_thread_is_being_profiled_for_markers());
100 auto res = GetNvidiaVpSuperResolutionInfo(aVideoContext, aVideoProcessor);
101 if (res.isErr()) {
102 return;
105 auto data = res.unwrap();
107 nsPrintfCString str(
108 "SuperResolution VP Capable %u OtherFieldsValid %u Enabled %u InUse %u "
109 "Level %u",
110 data.vsrGPUisVSRCapable, data.vsrOtherFieldsValid, data.vsrEnabled,
111 data.vsrIsInUseForThisVP, data.vsrLevel);
112 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str);
115 static HRESULT SetNvidiaVpSuperResolution(ID3D11VideoContext* aVideoContext,
116 ID3D11VideoProcessor* aVideoProcessor,
117 bool aEnable) {
118 LOG("SetNvidiaVpSuperResolution() aEnable=%d", aEnable);
120 // Undocumented NVIDIA driver constants
121 constexpr GUID nvGUID = {0xD43CE1B3,
122 0x1F4B,
123 0x48AC,
124 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
126 constexpr UINT nvExtensionVersion = 0x1;
127 constexpr UINT nvExtensionMethodSuperResolution = 0x2;
128 struct {
129 UINT version;
130 UINT method;
131 UINT enable;
132 } streamExtensionInfo = {nvExtensionVersion, nvExtensionMethodSuperResolution,
133 aEnable ? 1u : 0};
135 HRESULT hr;
136 hr = aVideoContext->VideoProcessorSetStreamExtension(
137 aVideoProcessor, 0, &nvGUID, sizeof(streamExtensionInfo),
138 &streamExtensionInfo);
139 return hr;
142 static HRESULT SetVpSuperResolution(UINT aGpuVendorId,
143 ID3D11VideoContext* aVideoContext,
144 ID3D11VideoProcessor* aVideoProcessor,
145 bool aEnable) {
146 MOZ_ASSERT(aVideoContext);
147 MOZ_ASSERT(aVideoProcessor);
149 if (aGpuVendorId == 0x10DE) {
150 return SetNvidiaVpSuperResolution(aVideoContext, aVideoProcessor, aEnable);
152 return E_NOTIMPL;
155 static bool GetNvidiaRTXVideoTrueHDRSupported(
156 ID3D11VideoContext* aVideoContext, ID3D11VideoProcessor* aVideoProcessor) {
157 const GUID kNvidiaTrueHDRInterfaceGUID = {
158 0xfdd62bb4,
159 0x620b,
160 0x4fd7,
161 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
162 UINT available = 0;
163 HRESULT hr = aVideoContext->VideoProcessorGetStreamExtension(
164 aVideoProcessor, 0, &kNvidiaTrueHDRInterfaceGUID, sizeof(available),
165 &available);
166 if (FAILED(hr)) {
167 return false;
170 bool driverSupportsTrueHdr = (available == 1);
171 return driverSupportsTrueHdr;
174 static HRESULT SetNvidiaRTXVideoTrueHDR(ID3D11VideoContext* aVideoContext,
175 ID3D11VideoProcessor* aVideoProcessor,
176 bool aEnable) {
177 constexpr GUID kNvidiaTrueHDRInterfaceGUID = {
178 0xfdd62bb4,
179 0x620b,
180 0x4fd7,
181 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}};
182 constexpr UINT kStreamExtensionMethodTrueHDR = 0x3;
183 const UINT TrueHDRVersion4 = 4;
184 struct {
185 UINT version;
186 UINT method;
187 UINT enable : 1;
188 UINT reserved : 31;
189 } streamExtensionInfo = {TrueHDRVersion4, kStreamExtensionMethodTrueHDR,
190 aEnable ? 1u : 0u, 0u};
191 HRESULT hr = aVideoContext->VideoProcessorSetStreamExtension(
192 aVideoProcessor, 0, &kNvidiaTrueHDRInterfaceGUID,
193 sizeof(streamExtensionInfo), &streamExtensionInfo);
194 return hr;
197 static bool GetVpAutoHDRSupported(UINT aGpuVendorId,
198 ID3D11VideoContext* aVideoContext,
199 ID3D11VideoProcessor* aVideoProcessor) {
200 MOZ_ASSERT(aVideoContext);
201 MOZ_ASSERT(aVideoProcessor);
203 if (aGpuVendorId == 0x10DE) {
204 return GetNvidiaRTXVideoTrueHDRSupported(aVideoContext, aVideoProcessor);
206 return false;
209 static HRESULT SetVpAutoHDR(UINT aGpuVendorId,
210 ID3D11VideoContext* aVideoContext,
211 ID3D11VideoProcessor* aVideoProcessor,
212 bool aEnable) {
213 MOZ_ASSERT(aVideoContext);
214 MOZ_ASSERT(aVideoProcessor);
216 if (aGpuVendorId == 0x10DE) {
217 return SetNvidiaRTXVideoTrueHDR(aVideoContext, aVideoProcessor, aEnable);
219 MOZ_ASSERT_UNREACHABLE("Unexpected to be called");
220 return E_NOTIMPL;
223 StaticAutoPtr<GpuOverlayInfo> DCLayerTree::sGpuOverlayInfo;
225 /* static */
226 UniquePtr<DCLayerTree> DCLayerTree::Create(gl::GLContext* aGL,
227 EGLConfig aEGLConfig,
228 ID3D11Device* aDevice,
229 ID3D11DeviceContext* aCtx,
230 HWND aHwnd, nsACString& aError) {
231 RefPtr<IDCompositionDevice2> dCompDevice =
232 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
233 if (!dCompDevice) {
234 aError.Assign("DCLayerTree(no device)"_ns);
235 return nullptr;
238 auto layerTree = MakeUnique<DCLayerTree>(aGL, aEGLConfig, aDevice, aCtx,
239 aHwnd, dCompDevice);
240 if (!layerTree->Initialize(aHwnd, aError)) {
241 return nullptr;
244 return layerTree;
247 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo = nullptr; }
249 DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
250 ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
251 HWND aHwnd, IDCompositionDevice2* aCompositionDevice)
252 : mGL(aGL),
253 mEGLConfig(aEGLConfig),
254 mDevice(aDevice),
255 mCtx(aCtx),
256 mHwnd(aHwnd),
257 mCompositionDevice(aCompositionDevice),
258 mDebugCounter(false),
259 mDebugVisualRedrawRegions(false),
260 mEGLImage(EGL_NO_IMAGE),
261 mColorRBO(0),
262 mPendingCommit(false) {
263 LOG("DCLayerTree::DCLayerTree()");
266 DCLayerTree::~DCLayerTree() {
267 LOG("DCLayerTree::~DCLayerTree()");
269 ReleaseNativeCompositorResources();
272 void DCLayerTree::ReleaseNativeCompositorResources() {
273 const auto gl = GetGLContext();
275 DestroyEGLSurface();
277 // Delete any cached FBO objects
278 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
279 gl->fDeleteRenderbuffers(1, &it->depthRboId);
280 gl->fDeleteFramebuffers(1, &it->fboId);
284 bool DCLayerTree::Initialize(HWND aHwnd, nsACString& aError) {
285 HRESULT hr;
287 RefPtr<IDCompositionDesktopDevice> desktopDevice;
288 hr = mCompositionDevice->QueryInterface(
289 (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice));
290 if (FAILED(hr)) {
291 aError.Assign(nsPrintfCString(
292 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr));
293 return false;
296 hr = desktopDevice->CreateTargetForHwnd(aHwnd, TRUE,
297 getter_AddRefs(mCompositionTarget));
298 if (FAILED(hr)) {
299 aError.Assign(nsPrintfCString(
300 "DCLayerTree(create DCompositionTarget failed %lx)", hr));
301 return false;
304 hr = mCompositionDevice->CreateVisual(getter_AddRefs(mRootVisual));
305 if (FAILED(hr)) {
306 aError.Assign(nsPrintfCString(
307 "DCLayerTree(create root DCompositionVisual failed %lx)", hr));
308 return false;
311 hr =
312 mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual));
313 if (FAILED(hr)) {
314 aError.Assign(nsPrintfCString(
315 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr));
316 return false;
319 if (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin() ||
320 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) {
321 if (!InitializeVideoOverlaySupport()) {
322 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY);
325 if (!sGpuOverlayInfo) {
326 // Set default if sGpuOverlayInfo was not set.
327 sGpuOverlayInfo = new GpuOverlayInfo();
330 // Initialize SwapChainInfo
331 SupportsSwapChainTearing();
333 mCompositionTarget->SetRoot(mRootVisual);
334 // Set interporation mode to nearest, to ensure 1:1 sampling.
335 // By default, a visual inherits the interpolation mode of the parent visual.
336 // If no visuals set the interpolation mode, the default for the entire visual
337 // tree is nearest neighbor interpolation.
338 mRootVisual->SetBitmapInterpolationMode(
339 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
340 return true;
343 bool FlagsSupportsOverlays(UINT flags) {
344 return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
345 DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
348 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
349 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat,
350 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace,
351 RefPtr<IDXGIOutput> aOutput,
352 RefPtr<ID3D11Device> aD3d11Device) {
353 UINT colorSpaceSupportFlags = 0;
354 RefPtr<IDXGIOutput4> output4;
356 if (FAILED(aOutput->QueryInterface(__uuidof(IDXGIOutput4),
357 getter_AddRefs(output4)))) {
358 return false;
361 if (FAILED(output4->CheckOverlayColorSpaceSupport(
362 aDxgiFormat, aDxgiColorSpace, aD3d11Device,
363 &colorSpaceSupportFlags))) {
364 return false;
367 return (colorSpaceSupportFlags &
368 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
371 bool DCLayerTree::InitializeVideoOverlaySupport() {
372 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
374 HRESULT hr;
376 hr = mDevice->QueryInterface(
377 (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice));
378 if (FAILED(hr)) {
379 gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr);
380 return false;
383 hr =
384 mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext));
385 if (FAILED(hr)) {
386 gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr);
387 return false;
390 if (sGpuOverlayInfo) {
391 return true;
394 UniquePtr<GpuOverlayInfo> info = MakeUnique<GpuOverlayInfo>();
396 RefPtr<IDXGIDevice> dxgiDevice;
397 RefPtr<IDXGIAdapter> adapter;
398 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
399 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
401 unsigned int i = 0;
402 while (true) {
403 RefPtr<IDXGIOutput> output;
404 if (FAILED(adapter->EnumOutputs(i++, getter_AddRefs(output)))) {
405 break;
407 RefPtr<IDXGIOutput3> output3;
408 if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput3),
409 getter_AddRefs(output3)))) {
410 break;
413 output3->CheckOverlaySupport(DXGI_FORMAT_NV12, mDevice,
414 &info->mNv12OverlaySupportFlags);
415 output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, mDevice,
416 &info->mYuy2OverlaySupportFlags);
417 output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, mDevice,
418 &info->mBgra8OverlaySupportFlags);
419 output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM, mDevice,
420 &info->mRgb10a2OverlaySupportFlags);
422 if (FlagsSupportsOverlays(info->mNv12OverlaySupportFlags)) {
423 // NV12 format is preferred if it's supported.
424 info->mOverlayFormatUsed = DXGI_FORMAT_NV12;
425 info->mSupportsHardwareOverlays = true;
428 if (!info->mSupportsHardwareOverlays &&
429 FlagsSupportsOverlays(info->mYuy2OverlaySupportFlags)) {
430 // If NV12 isn't supported, fallback to YUY2 if it's supported.
431 info->mOverlayFormatUsed = DXGI_FORMAT_YUY2;
432 info->mSupportsHardwareOverlays = true;
435 // RGB10A2 overlay is used for displaying HDR content. In Intel's
436 // platform, RGB10A2 overlay is enabled only when
437 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
438 if (FlagsSupportsOverlays(info->mRgb10a2OverlaySupportFlags)) {
439 if (!CheckOverlayColorSpaceSupport(
440 DXGI_FORMAT_R10G10B10A2_UNORM,
441 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, mDevice))
442 info->mRgb10a2OverlaySupportFlags = 0;
445 // Early out after the first output that reports overlay support. All
446 // outputs are expected to report the same overlay support according to
447 // Microsoft's WDDM documentation:
448 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
449 if (info->mSupportsHardwareOverlays) {
450 break;
454 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
455 info->mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
456 info->mSupportsHardwareOverlays = false;
459 info->mSupportsOverlays = info->mSupportsHardwareOverlays;
461 // Check VpSuperResolution and VpAutoHDR support.
462 const auto size = gfx::IntSize(100, 100);
463 if (EnsureVideoProcessor(size, size)) {
464 const UINT vendorId = GetVendorId(mVideoDevice);
465 if (vendorId == 0x10DE) {
466 auto res = GetNvidiaVpSuperResolutionInfo(mVideoContext, mVideoProcessor);
467 if (res.isOk() && res.unwrap().vsrGPUisVSRCapable) {
468 info->mSupportsVpSuperResolution = true;
472 const bool driverSupportVpAutoHDR =
473 GetVpAutoHDRSupported(vendorId, mVideoContext, mVideoProcessor);
474 if (driverSupportVpAutoHDR) {
475 info->mSupportsVpAutoHDR = true;
479 // Note: "UniquePtr::release" here is saying "release your ownership stake
480 // on your pointer, so that our StaticAutoPtr can take over ownership".
481 // (StaticAutoPtr doesn't have a move constructor that could directly steal
482 // the contents of a UniquePtr via std::move().)
483 sGpuOverlayInfo = info.release();
485 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
486 gpuParent->NotifyOverlayInfo(GetOverlayInfo());
489 return true;
492 DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const {
493 auto surface_it = mDCSurfaces.find(aId);
494 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
495 return surface_it->second.get();
498 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) {
499 LOG("DCLayerTree::SetDefaultSwapChain()");
501 mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr);
502 mDefaultSwapChainVisual->SetContent(aSwapChain);
503 // Default SwapChain's visual does not need linear interporation.
504 mDefaultSwapChainVisual->SetBitmapInterpolationMode(
505 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
506 mPendingCommit = true;
509 void DCLayerTree::MaybeUpdateDebug() {
510 bool updated = false;
511 updated |= MaybeUpdateDebugCounter();
512 updated |= MaybeUpdateDebugVisualRedrawRegions();
513 if (updated) {
514 mPendingCommit = true;
518 void DCLayerTree::MaybeCommit() {
519 if (!mPendingCommit) {
520 return;
522 mCompositionDevice->Commit();
525 void DCLayerTree::WaitForCommitCompletion() {
526 // To ensure that swapchain layers have presented to the screen
527 // for capture, call present twice. This is less than ideal, but
528 // I'm not sure if there is a better way to ensure this syncs
529 // correctly that works on both Win10/11. Even though this can
530 // be slower than necessary, it's only used by the reftest
531 // screenshotting code, so isn't particularly perf sensitive.
532 for (auto it = mDCSurfaces.begin(); it != mDCSurfaces.end(); it++) {
533 auto* surface = it->second->AsDCSwapChain();
534 if (surface) {
535 surface->Present();
536 surface->Present();
540 mCompositionDevice->WaitForCommitCompletion();
543 void DCLayerTree::DisableNativeCompositor() {
544 MOZ_ASSERT(mCurrentSurface.isNothing());
545 MOZ_ASSERT(mCurrentLayers.empty());
547 ReleaseNativeCompositorResources();
548 mPrevLayers.clear();
549 mRootVisual->RemoveAllVisuals();
552 bool DCLayerTree::MaybeUpdateDebugCounter() {
553 bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter();
554 if (mDebugCounter == debugCounter) {
555 return false;
558 RefPtr<IDCompositionDeviceDebug> debugDevice;
559 HRESULT hr = mCompositionDevice->QueryInterface(
560 (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
561 if (FAILED(hr)) {
562 return false;
565 if (debugCounter) {
566 debugDevice->EnableDebugCounters();
567 } else {
568 debugDevice->DisableDebugCounters();
571 mDebugCounter = debugCounter;
572 return true;
575 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
576 bool debugVisualRedrawRegions =
577 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
578 if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) {
579 return false;
582 RefPtr<IDCompositionVisualDebug> visualDebug;
583 HRESULT hr = mRootVisual->QueryInterface(
584 (IDCompositionVisualDebug**)getter_AddRefs(visualDebug));
585 if (FAILED(hr)) {
586 return false;
589 if (debugVisualRedrawRegions) {
590 visualDebug->EnableRedrawRegions();
591 } else {
592 visualDebug->DisableRedrawRegions();
595 mDebugVisualRedrawRegions = debugVisualRedrawRegions;
596 return true;
599 void DCLayerTree::CompositorBeginFrame() {
600 mCurrentFrame++;
601 mUsedOverlayTypesInFrame = DCompOverlayTypes::NO_OVERLAY;
604 void DCLayerTree::CompositorEndFrame() {
605 auto start = TimeStamp::Now();
606 // Check if the visual tree of surfaces is the same as last frame.
607 bool same = mPrevLayers == mCurrentLayers;
609 if (!same) {
610 // If not, we need to rebuild the visual tree. Note that addition or
611 // removal of tiles no longer needs to rebuild the main visual tree
612 // here, since they are added as children of the surface visual.
613 mRootVisual->RemoveAllVisuals();
616 for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) {
617 auto surface_it = mDCSurfaces.find(*it);
618 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
619 const auto surface = surface_it->second.get();
620 // Ensure surface is trimmed to updated tile valid rects
621 surface->UpdateAllocatedRect();
622 if (!same) {
623 // Add surfaces in z-order they were added to the scene.
624 const auto visual = surface->GetVisual();
625 mRootVisual->AddVisual(visual, false, nullptr);
629 mPrevLayers.swap(mCurrentLayers);
630 mCurrentLayers.clear();
632 mCompositionDevice->Commit();
634 auto end = TimeStamp::Now();
635 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
636 (end - start).ToMilliseconds() * 10.);
638 // Remove any framebuffers that haven't been
639 // used in the last 60 frames.
641 // This should use nsTArray::RemoveElementsBy once
642 // CachedFrameBuffer is able to properly destroy
643 // itself in the destructor.
644 const auto gl = GetGLContext();
645 for (uint32_t i = 0, len = mFrameBuffers.Length(); i < len; ++i) {
646 auto& fb = mFrameBuffers[i];
647 if ((mCurrentFrame - fb.lastFrameUsed) > 60) {
648 gl->fDeleteRenderbuffers(1, &fb.depthRboId);
649 gl->fDeleteFramebuffers(1, &fb.fboId);
650 mFrameBuffers.UnorderedRemoveElementAt(i);
651 --i; // Examine the element again, if necessary.
652 --len;
656 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
657 return;
660 // Disable video overlay if mCompositionDevice->Commit() with video overlay is
661 // too slow. It drops fps.
663 const auto maxCommitWaitDurationMs = 20;
664 const auto maxSlowCommitCount = 5;
665 const auto commitDurationMs =
666 static_cast<uint32_t>((end - start).ToMilliseconds());
668 nsPrintfCString marker("CommitWait overlay %u %ums ",
669 (uint8_t)mUsedOverlayTypesInFrame, commitDurationMs);
670 PROFILER_MARKER_TEXT("CommitWait", GRAPHICS, {}, marker);
672 if (mUsedOverlayTypesInFrame != DCompOverlayTypes::NO_OVERLAY &&
673 commitDurationMs > maxCommitWaitDurationMs) {
674 mSlowCommitCount++;
675 } else {
676 mSlowCommitCount = 0;
679 if (mSlowCommitCount <= maxSlowCommitCount) {
680 return;
683 for (auto it = mDCSurfaces.begin(); it != mDCSurfaces.end(); it++) {
684 auto* surfaceVideo = it->second->AsDCSurfaceVideo();
685 if (surfaceVideo) {
686 surfaceVideo->DisableVideoOverlay();
690 if (mUsedOverlayTypesInFrame & DCompOverlayTypes::SOFTWARE_DECODED_VIDEO) {
691 gfxCriticalNoteOnce << "Sw video swapchain present is slow";
693 nsPrintfCString marker("Sw video swapchain present is slow");
694 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
696 if (mUsedOverlayTypesInFrame & DCompOverlayTypes::HARDWARE_DECODED_VIDEO) {
697 gfxCriticalNoteOnce << "Hw video swapchain present is slow";
699 nsPrintfCString marker("Hw video swapchain present is slow");
700 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
704 void DCLayerTree::BindSwapChain(wr::NativeSurfaceId aId) {
705 auto surface = GetSurface(aId);
706 surface->AsDCSwapChain()->Bind();
709 void DCLayerTree::PresentSwapChain(wr::NativeSurfaceId aId) {
710 auto surface = GetSurface(aId);
711 surface->AsDCSwapChain()->Present();
714 void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
715 uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
716 wr::DeviceIntRect aValidRect) {
717 auto surface = GetSurface(aId.surface_id);
718 auto tile = surface->GetTile(aId.x, aId.y);
719 wr::DeviceIntPoint targetOffset{0, 0};
721 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
722 // IDCompositionVirtualSurface owned by the DCSurface.
723 RefPtr<IDCompositionSurface> compositionSurface;
724 if (surface->mIsVirtualSurface) {
725 gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y,
726 aValidRect.width(), aValidRect.height());
727 if (!tile->mValidRect.IsEqualEdges(validRect)) {
728 tile->mValidRect = validRect;
729 surface->DirtyAllocatedRect();
731 wr::DeviceIntSize tileSize = surface->GetTileSize();
732 compositionSurface = surface->GetCompositionSurface();
733 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
734 targetOffset.x = virtualOffset.x + tileSize.width * aId.x;
735 targetOffset.y = virtualOffset.y + tileSize.height * aId.y;
736 } else {
737 compositionSurface = tile->Bind(aValidRect);
740 if (tile->mNeedsFullDraw) {
741 // dcomp requires that the first BeginDraw on a non-virtual surface is the
742 // full size of the pixel buffer.
743 auto tileSize = surface->GetTileSize();
744 aDirtyRect.min.x = 0;
745 aDirtyRect.min.y = 0;
746 aDirtyRect.max.x = tileSize.width;
747 aDirtyRect.max.y = tileSize.height;
748 tile->mNeedsFullDraw = false;
751 *aFboId = CreateEGLSurfaceForCompositionSurface(
752 aDirtyRect, aOffset, compositionSurface, targetOffset);
753 mCurrentSurface = Some(compositionSurface);
756 void DCLayerTree::Unbind() {
757 if (mCurrentSurface.isNothing()) {
758 return;
761 RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref();
762 surface->EndDraw();
764 DestroyEGLSurface();
765 mCurrentSurface = Nothing();
768 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
769 wr::DeviceIntPoint aVirtualOffset,
770 wr::DeviceIntSize aTileSize, bool aIsOpaque) {
771 auto it = mDCSurfaces.find(aId);
772 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
773 if (it != mDCSurfaces.end()) {
774 // DCSurface already exists.
775 return;
778 // Tile size needs to be positive.
779 if (aTileSize.width <= 0 || aTileSize.height <= 0) {
780 gfxCriticalNote << "TileSize is not positive aId: " << wr::AsUint64(aId)
781 << " aTileSize(" << aTileSize.width << ","
782 << aTileSize.height << ")";
785 bool isVirtualSurface =
786 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
787 auto surface = MakeUnique<DCSurface>(aTileSize, aVirtualOffset,
788 isVirtualSurface, aIsOpaque, this);
789 if (!surface->Initialize()) {
790 gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId);
791 return;
794 mDCSurfaces[aId] = std::move(surface);
797 void DCLayerTree::CreateSwapChainSurface(wr::NativeSurfaceId aId,
798 wr::DeviceIntSize aSize,
799 bool aIsOpaque) {
800 auto it = mDCSurfaces.find(aId);
801 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
803 auto surface = MakeUnique<DCSwapChain>(aSize, aIsOpaque, this);
804 if (!surface->Initialize()) {
805 gfxCriticalNote << "Failed to initialize DCSwapChain: "
806 << wr::AsUint64(aId);
807 return;
810 mDCSurfaces[aId] = std::move(surface);
813 void DCLayerTree::ResizeSwapChainSurface(wr::NativeSurfaceId aId,
814 wr::DeviceIntSize aSize) {
815 auto it = mDCSurfaces.find(aId);
816 MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
817 auto surface = it->second.get();
819 surface->AsDCSwapChain()->Resize(aSize);
822 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId,
823 bool aIsOpaque) {
824 auto it = mDCSurfaces.find(aId);
825 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
827 auto surface = MakeUnique<DCExternalSurfaceWrapper>(aIsOpaque, this);
828 if (!surface->Initialize()) {
829 gfxCriticalNote << "Failed to initialize DCExternalSurfaceWrapper: "
830 << wr::AsUint64(aId);
831 return;
834 mDCSurfaces[aId] = std::move(surface);
837 void DCLayerTree::DestroySurface(NativeSurfaceId aId) {
838 auto surface_it = mDCSurfaces.find(aId);
839 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
840 auto surface = surface_it->second.get();
842 mRootVisual->RemoveVisual(surface->GetVisual());
843 mDCSurfaces.erase(surface_it);
846 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
847 auto surface = GetSurface(aId);
848 surface->CreateTile(aX, aY);
851 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
852 auto surface = GetSurface(aId);
853 surface->DestroyTile(aX, aY);
856 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId,
857 wr::ExternalImageId aExternalImage) {
858 auto surface_it = mDCSurfaces.find(aId);
859 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
860 surface_it->second->AttachExternalImage(aExternalImage);
863 void DCExternalSurfaceWrapper::AttachExternalImage(
864 wr::ExternalImageId aExternalImage) {
865 if (auto* surface = EnsureSurfaceForExternalImage(aExternalImage)) {
866 surface->AttachExternalImage(aExternalImage);
870 template <class ToT>
871 struct QI {
872 template <class FromT>
873 [[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) {
874 RefPtr<ToT> to;
875 (void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to)));
876 return to;
880 DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
881 wr::ExternalImageId aExternalImage) {
882 if (mSurface) {
883 return mSurface.get();
886 // Create a new surface based on the texture type.
887 RenderTextureHost* texture =
888 RenderThread::Get()->GetRenderTexture(aExternalImage);
889 if (texture && texture->AsRenderDXGITextureHost()) {
890 mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree));
891 if (!mSurface->Initialize()) {
892 gfxCriticalNote << "Failed to initialize DCSurfaceVideo: "
893 << wr::AsUint64(aExternalImage);
894 mSurface = nullptr;
896 } else if (texture && texture->AsRenderDcompSurfaceTextureHost()) {
897 mSurface.reset(new DCSurfaceHandle(mIsOpaque, mDCLayerTree));
898 if (!mSurface->Initialize()) {
899 gfxCriticalNote << "Failed to initialize DCSurfaceHandle: "
900 << wr::AsUint64(aExternalImage);
901 mSurface = nullptr;
904 if (!mSurface) {
905 gfxCriticalNote << "Failed to create a surface for external image: "
906 << gfx::hexa(texture);
907 return nullptr;
910 // Add surface's visual which will contain video data to our root visual.
911 const auto surfaceVisual = mSurface->GetVisual();
912 mVisual->AddVisual(surfaceVisual, true, nullptr);
914 // -
915 // Apply color management.
917 [&]() {
918 if (!StaticPrefs::gfx_webrender_dcomp_color_manage_with_filters()) return;
920 const auto cmsMode = GfxColorManagementMode();
921 if (cmsMode == CMSMode::Off) return;
923 const auto dcomp = mDCLayerTree->GetCompositionDevice();
924 const auto dcomp3 = QI<IDCompositionDevice3>::From(dcomp);
925 if (!dcomp3) {
926 NS_WARNING(
927 "No IDCompositionDevice3, cannot use dcomp for color management.");
928 return;
931 // -
933 const auto cspace = [&]() {
934 const auto rangedCspace = texture->GetYUVColorSpace();
935 const auto info = FromYUVRangedColorSpace(rangedCspace);
936 auto ret = ToColorSpace2(info.space);
937 if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) {
938 ret = gfx::ColorSpace2::SRGB;
940 return ret;
941 }();
943 const bool rec709GammaAsSrgb =
944 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
945 const bool rec2020GammaAsRec709 =
946 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
948 auto cspaceDesc = color::ColorspaceDesc{};
949 switch (cspace) {
950 case gfx::ColorSpace2::Display:
951 return; // No color management needed!
952 case gfx::ColorSpace2::SRGB:
953 cspaceDesc.chrom = color::Chromaticities::Srgb();
954 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
955 break;
957 case gfx::ColorSpace2::DISPLAY_P3:
958 cspaceDesc.chrom = color::Chromaticities::DisplayP3();
959 cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3();
960 break;
962 case gfx::ColorSpace2::BT601_525:
963 cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc();
964 if (rec709GammaAsSrgb) {
965 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
966 } else {
967 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
969 break;
971 case gfx::ColorSpace2::BT709:
972 cspaceDesc.chrom = color::Chromaticities::Rec709();
973 if (rec709GammaAsSrgb) {
974 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
975 } else {
976 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
978 break;
980 case gfx::ColorSpace2::BT2020:
981 cspaceDesc.chrom = color::Chromaticities::Rec2020();
982 if (rec2020GammaAsRec709 && rec709GammaAsSrgb) {
983 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
984 } else if (rec2020GammaAsRec709) {
985 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
986 } else {
987 // Just Rec709 with slightly more precision.
988 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit();
990 break;
993 const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc);
994 auto cprofileOut = mDCLayerTree->OutputColorProfile();
995 bool pretendSrgb = true;
996 if (pretendSrgb) {
997 cprofileOut = color::ColorProfileDesc::From(color::ColorspaceDesc{
998 .chrom = color::Chromaticities::Srgb(),
999 .tf = color::PiecewiseGammaDesc::Srgb(),
1002 const auto conversion = color::ColorProfileConversionDesc::From({
1003 .src = cprofileIn,
1004 .dst = cprofileOut,
1007 // -
1009 auto chain = ColorManagementChain::From(*dcomp3, conversion);
1010 mCManageChain = Some(chain);
1012 surfaceVisual->SetEffect(mCManageChain->last.get());
1013 }();
1015 return mSurface.get();
1018 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix& aTransform) {
1019 MOZ_ASSERT(mSurface);
1020 if (auto* surface = mSurface->AsDCSurfaceVideo()) {
1021 if (surface->CalculateSwapChainSize(aTransform)) {
1022 surface->PresentVideo();
1024 } else if (auto* surface = mSurface->AsDCSurfaceHandle()) {
1025 surface->PresentSurfaceHandle();
1029 template <typename T>
1030 static inline D2D1_RECT_F D2DRect(const T& aRect) {
1031 return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost());
1034 static inline D2D1_MATRIX_3X2_F D2DMatrix(const gfx::Matrix& aTransform) {
1035 return D2D1::Matrix3x2F(aTransform._11, aTransform._12, aTransform._21,
1036 aTransform._22, aTransform._31, aTransform._32);
1039 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
1040 const wr::CompositorSurfaceTransform& aTransform,
1041 wr::DeviceIntRect aClipRect,
1042 wr::ImageRendering aImageRendering) {
1043 auto it = mDCSurfaces.find(aId);
1044 MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
1045 const auto surface = it->second.get();
1046 const auto visual = surface->GetVisual();
1048 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
1050 float sx = aTransform.scale.x;
1051 float sy = aTransform.scale.y;
1052 float tx = aTransform.offset.x;
1053 float ty = aTransform.offset.y;
1054 gfx::Matrix transform(sx, 0.0, 0.0, sy, tx, ty);
1056 surface->PresentExternalSurface(transform);
1058 transform.PreTranslate(-virtualOffset.x, -virtualOffset.y);
1060 // The DirectComposition API applies clipping *before* any
1061 // transforms/offset, whereas we want the clip applied after. Right now, we
1062 // only support rectilinear transforms, and then we transform our clip into
1063 // pre-transform coordinate space for it to be applied there.
1064 // DirectComposition does have an option for pre-transform clipping, if you
1065 // create an explicit IDCompositionEffectGroup object and set a 3D transform
1066 // on that. I suspect that will perform worse though, so we should only do
1067 // that for complex transforms (which are never provided right now).
1068 MOZ_ASSERT(transform.IsRectilinear());
1069 gfx::Rect clip = transform.Inverse().TransformBounds(gfx::Rect(
1070 aClipRect.min.x, aClipRect.min.y, aClipRect.width(), aClipRect.height()));
1071 // Set the clip rect - converting from world space to the pre-offset space
1072 // that DC requires for rectangle clips.
1073 visual->SetClip(D2DRect(clip));
1075 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
1076 // the D3D API level (unless we QI to IDCompositionVisual3, which might
1077 // not be available?).
1078 // Should we assert here, or restrict at the WR API level.
1079 visual->SetTransform(D2DMatrix(transform));
1081 if (aImageRendering == wr::ImageRendering::Auto) {
1082 visual->SetBitmapInterpolationMode(
1083 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
1084 } else {
1085 visual->SetBitmapInterpolationMode(
1086 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
1089 mCurrentLayers.push_back(aId);
1092 GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) {
1093 const auto gl = GetGLContext();
1094 GLuint fboId = 0;
1096 // Check if we have a cached FBO with matching dimensions
1097 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
1098 if (it->width == aWidth && it->height == aHeight) {
1099 fboId = it->fboId;
1100 it->lastFrameUsed = mCurrentFrame;
1101 break;
1105 // If not, create a new FBO with attached depth buffer
1106 if (fboId == 0) {
1107 // Create the depth buffer
1108 GLuint depthRboId;
1109 gl->fGenRenderbuffers(1, &depthRboId);
1110 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRboId);
1111 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_DEPTH_COMPONENT24,
1112 aWidth, aHeight);
1114 // Create the framebuffer and attach the depth buffer to it
1115 gl->fGenFramebuffers(1, &fboId);
1116 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
1117 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1118 LOCAL_GL_DEPTH_ATTACHMENT,
1119 LOCAL_GL_RENDERBUFFER, depthRboId);
1121 // Store this in the cache for future calls.
1122 // TODO(gw): Maybe we should periodically scan this list and remove old
1123 // entries that
1124 // haven't been used for some time?
1125 DCLayerTree::CachedFrameBuffer frame_buffer_info;
1126 frame_buffer_info.width = aWidth;
1127 frame_buffer_info.height = aHeight;
1128 frame_buffer_info.fboId = fboId;
1129 frame_buffer_info.depthRboId = depthRboId;
1130 frame_buffer_info.lastFrameUsed = mCurrentFrame;
1131 mFrameBuffers.AppendElement(frame_buffer_info);
1134 return fboId;
1137 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aInputSize,
1138 const gfx::IntSize& aOutputSize) {
1139 HRESULT hr;
1141 if (!mVideoDevice || !mVideoContext) {
1142 return false;
1145 if (mVideoProcessor && (aInputSize <= mVideoInputSize) &&
1146 (aOutputSize <= mVideoOutputSize)) {
1147 return true;
1150 mVideoProcessor = nullptr;
1151 mVideoProcessorEnumerator = nullptr;
1153 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {};
1154 desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
1155 desc.InputFrameRate.Numerator = 60;
1156 desc.InputFrameRate.Denominator = 1;
1157 desc.InputWidth = aInputSize.width;
1158 desc.InputHeight = aInputSize.height;
1159 desc.OutputFrameRate.Numerator = 60;
1160 desc.OutputFrameRate.Denominator = 1;
1161 desc.OutputWidth = aOutputSize.width;
1162 desc.OutputHeight = aOutputSize.height;
1163 desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
1165 hr = mVideoDevice->CreateVideoProcessorEnumerator(
1166 &desc, getter_AddRefs(mVideoProcessorEnumerator));
1167 if (FAILED(hr)) {
1168 gfxCriticalNote << "Failed to create VideoProcessorEnumerator: "
1169 << gfx::hexa(hr);
1170 return false;
1173 hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0,
1174 getter_AddRefs(mVideoProcessor));
1175 if (FAILED(hr)) {
1176 mVideoProcessor = nullptr;
1177 mVideoProcessorEnumerator = nullptr;
1178 gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr);
1179 return false;
1182 // Reduce power cosumption
1183 // By default, the driver might perform certain processing tasks
1184 // automatically
1185 mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0,
1186 FALSE);
1188 mVideoInputSize = aInputSize;
1189 mVideoOutputSize = aOutputSize;
1191 return true;
1194 bool DCLayerTree::SupportsHardwareOverlays() {
1195 return sGpuOverlayInfo->mSupportsHardwareOverlays;
1198 bool DCLayerTree::SupportsSwapChainTearing() {
1199 RefPtr<ID3D11Device> device = mDevice;
1200 static const bool supported = [device] {
1201 RefPtr<IDXGIDevice> dxgiDevice;
1202 RefPtr<IDXGIAdapter> adapter;
1203 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
1204 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
1206 RefPtr<IDXGIFactory5> dxgiFactory;
1207 HRESULT hr = adapter->GetParent(
1208 IID_PPV_ARGS((IDXGIFactory5**)getter_AddRefs(dxgiFactory)));
1209 if (FAILED(hr)) {
1210 return false;
1213 BOOL presentAllowTearing = FALSE;
1214 hr = dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
1215 &presentAllowTearing,
1216 sizeof(presentAllowTearing));
1217 if (FAILED(hr)) {
1218 return false;
1221 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
1222 gpuParent->NotifySwapChainInfo(
1223 layers::SwapChainInfo(!!presentAllowTearing));
1224 } else if (XRE_IsParentProcess()) {
1225 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1227 return !!presentAllowTearing;
1228 }();
1229 return supported;
1232 DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() {
1233 return sGpuOverlayInfo->mOverlayFormatUsed;
1236 static layers::OverlaySupportType FlagsToOverlaySupportType(
1237 UINT aFlags, bool aSoftwareOverlaySupported) {
1238 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) {
1239 return layers::OverlaySupportType::Scaling;
1241 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT) {
1242 return layers::OverlaySupportType::Direct;
1244 if (aSoftwareOverlaySupported) {
1245 return layers::OverlaySupportType::Software;
1247 return layers::OverlaySupportType::None;
1250 layers::OverlayInfo DCLayerTree::GetOverlayInfo() {
1251 layers::OverlayInfo info;
1253 info.mSupportsOverlays = sGpuOverlayInfo->mSupportsHardwareOverlays;
1254 info.mNv12Overlay =
1255 FlagsToOverlaySupportType(sGpuOverlayInfo->mNv12OverlaySupportFlags,
1256 /* aSoftwareOverlaySupported */ false);
1257 info.mYuy2Overlay =
1258 FlagsToOverlaySupportType(sGpuOverlayInfo->mYuy2OverlaySupportFlags,
1259 /* aSoftwareOverlaySupported */ false);
1260 info.mBgra8Overlay =
1261 FlagsToOverlaySupportType(sGpuOverlayInfo->mBgra8OverlaySupportFlags,
1262 /* aSoftwareOverlaySupported */ true);
1263 info.mRgb10a2Overlay =
1264 FlagsToOverlaySupportType(sGpuOverlayInfo->mRgb10a2OverlaySupportFlags,
1265 /* aSoftwareOverlaySupported */ false);
1267 info.mSupportsVpSuperResolution = sGpuOverlayInfo->mSupportsVpSuperResolution;
1268 info.mSupportsVpAutoHDR = sGpuOverlayInfo->mSupportsVpAutoHDR;
1270 return info;
1273 void DCLayerTree::SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes) {
1274 mUsedOverlayTypesInFrame |= aTypes;
1277 DCSurface::DCSurface(wr::DeviceIntSize aTileSize,
1278 wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface,
1279 bool aIsOpaque, DCLayerTree* aDCLayerTree)
1280 : mIsVirtualSurface(aIsVirtualSurface),
1281 mDCLayerTree(aDCLayerTree),
1282 mTileSize(aTileSize),
1283 mIsOpaque(aIsOpaque),
1284 mAllocatedRectDirty(true),
1285 mVirtualOffset(aVirtualOffset) {}
1287 DCSurface::~DCSurface() {}
1289 bool DCSurface::Initialize() {
1290 // Create a visual for tiles to attach to, whether virtual or not.
1291 HRESULT hr;
1292 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1293 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
1294 if (FAILED(hr)) {
1295 gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
1296 return false;
1299 // If virtual surface is enabled, create and attach to visual, in this case
1300 // the tiles won't own visuals or surfaces.
1301 if (mIsVirtualSurface) {
1302 DXGI_ALPHA_MODE alpha_mode =
1303 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1305 hr = dCompDevice->CreateVirtualSurface(
1306 VIRTUAL_SURFACE_SIZE, VIRTUAL_SURFACE_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM,
1307 alpha_mode, getter_AddRefs(mVirtualSurface));
1308 MOZ_ASSERT(SUCCEEDED(hr));
1310 // Bind the surface memory to this visual
1311 hr = mVisual->SetContent(mVirtualSurface);
1312 MOZ_ASSERT(SUCCEEDED(hr));
1315 return true;
1318 void DCSurface::CreateTile(int32_t aX, int32_t aY) {
1319 TileKey key(aX, aY);
1320 MOZ_RELEASE_ASSERT(mDCTiles.find(key) == mDCTiles.end());
1322 auto tile = MakeUnique<DCTile>(mDCLayerTree);
1323 if (!tile->Initialize(aX, aY, mTileSize, mIsVirtualSurface, mIsOpaque,
1324 mVisual)) {
1325 gfxCriticalNote << "Failed to initialize DCTile: " << aX << aY;
1326 return;
1329 if (mIsVirtualSurface) {
1330 mAllocatedRectDirty = true;
1331 } else {
1332 mVisual->AddVisual(tile->GetVisual(), false, nullptr);
1335 mDCTiles[key] = std::move(tile);
1338 void DCSurface::DestroyTile(int32_t aX, int32_t aY) {
1339 TileKey key(aX, aY);
1340 if (mIsVirtualSurface) {
1341 mAllocatedRectDirty = true;
1342 } else {
1343 auto tile = GetTile(aX, aY);
1344 mVisual->RemoveVisual(tile->GetVisual());
1346 mDCTiles.erase(key);
1349 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty = true; }
1351 void DCSurface::UpdateAllocatedRect() {
1352 if (mAllocatedRectDirty) {
1353 if (mVirtualSurface) {
1354 // The virtual surface may have holes in it (for example, an empty tile
1355 // that has no primitives). Instead of trimming to a single bounding
1356 // rect, supply the rect of each valid tile to handle this case.
1357 std::vector<RECT> validRects;
1359 for (auto it = mDCTiles.begin(); it != mDCTiles.end(); ++it) {
1360 auto tile = GetTile(it->first.mX, it->first.mY);
1361 RECT rect;
1363 rect.left = (LONG)(mVirtualOffset.x + it->first.mX * mTileSize.width +
1364 tile->mValidRect.x);
1365 rect.top = (LONG)(mVirtualOffset.y + it->first.mY * mTileSize.height +
1366 tile->mValidRect.y);
1367 rect.right = rect.left + tile->mValidRect.width;
1368 rect.bottom = rect.top + tile->mValidRect.height;
1370 validRects.push_back(rect);
1373 mVirtualSurface->Trim(validRects.data(), validRects.size());
1375 // When not using a virtual surface, we still want to reset this
1376 mAllocatedRectDirty = false;
1380 DCTile* DCSurface::GetTile(int32_t aX, int32_t aY) const {
1381 TileKey key(aX, aY);
1382 auto tile_it = mDCTiles.find(key);
1383 MOZ_RELEASE_ASSERT(tile_it != mDCTiles.end());
1384 return tile_it->second.get();
1387 DCSwapChain::~DCSwapChain() {
1388 if (mEGLSurface) {
1389 const auto gl = mDCLayerTree->GetGLContext();
1391 const auto& gle = gl::GLContextEGL::Cast(gl);
1392 const auto& egl = gle->mEgl;
1393 egl->fDestroySurface(mEGLSurface);
1394 mEGLSurface = EGL_NO_SURFACE;
1398 bool DCSwapChain::Initialize() {
1399 DCSurface::Initialize();
1401 const auto gl = mDCLayerTree->GetGLContext();
1402 const auto& gle = gl::GLContextEGL::Cast(gl);
1403 const auto& egl = gle->mEgl;
1405 HRESULT hr;
1406 auto device = mDCLayerTree->GetDevice();
1408 RefPtr<IDXGIDevice> dxgiDevice;
1409 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
1411 RefPtr<IDXGIFactory2> dxgiFactory;
1413 RefPtr<IDXGIAdapter> adapter;
1414 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
1415 adapter->GetParent(
1416 IID_PPV_ARGS((IDXGIFactory2**)getter_AddRefs(dxgiFactory)));
1419 DXGI_SWAP_CHAIN_DESC1 desc{};
1420 desc.Width = mSize.width;
1421 desc.Height = mSize.height;
1422 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
1423 desc.SampleDesc.Count = 1;
1424 desc.SampleDesc.Quality = 0;
1425 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1426 desc.BufferCount = 2;
1427 // DXGI_SCALING_NONE caused swap chain creation failure.
1428 desc.Scaling = DXGI_SCALING_STRETCH;
1429 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1430 desc.AlphaMode =
1431 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1432 desc.Flags = 0;
1434 hr = dxgiFactory->CreateSwapChainForComposition(device, &desc, nullptr,
1435 getter_AddRefs(mSwapChain));
1436 MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
1437 mVisual->SetContent(mSwapChain);
1439 ID3D11Texture2D* backBuffer;
1440 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
1441 MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
1443 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, mSize.width, LOCAL_EGL_HEIGHT,
1444 mSize.height, LOCAL_EGL_NONE};
1445 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuffer);
1446 EGLConfig eglConfig = mDCLayerTree->GetEGLConfig();
1448 mEGLSurface = egl->fCreatePbufferFromClientBuffer(
1449 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, eglConfig, pbuffer_attribs);
1450 MOZ_RELEASE_ASSERT(mEGLSurface);
1451 backBuffer->Release();
1453 return true;
1456 void DCSwapChain::Bind() {
1457 const auto gl = mDCLayerTree->GetGLContext();
1458 const auto& gle = gl::GLContextEGL::Cast(gl);
1460 gle->SetEGLSurfaceOverride(mEGLSurface);
1461 bool ok = gl->MakeCurrent();
1463 MOZ_RELEASE_ASSERT(ok);
1466 void DCSwapChain::Resize(wr::DeviceIntSize aSize) {
1467 const auto gl = mDCLayerTree->GetGLContext();
1469 const auto& gle = gl::GLContextEGL::Cast(gl);
1470 const auto& egl = gle->mEgl;
1472 if (mEGLSurface) {
1473 egl->fDestroySurface(mEGLSurface);
1474 mEGLSurface = EGL_NO_SURFACE;
1477 ID3D11Texture2D* backBuffer;
1478 DXGI_SWAP_CHAIN_DESC desc;
1479 HRESULT hr;
1481 hr = mSwapChain->GetDesc(&desc);
1482 MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
1484 hr = mSwapChain->ResizeBuffers(desc.BufferCount, aSize.width, aSize.height,
1485 DXGI_FORMAT_B8G8R8A8_UNORM, 0);
1486 MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
1488 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
1489 MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
1491 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, aSize.width, LOCAL_EGL_HEIGHT,
1492 aSize.height, LOCAL_EGL_NONE};
1493 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuffer);
1494 EGLConfig eglConfig = mDCLayerTree->GetEGLConfig();
1496 mEGLSurface = egl->fCreatePbufferFromClientBuffer(
1497 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, eglConfig, pbuffer_attribs);
1498 MOZ_RELEASE_ASSERT(mEGLSurface);
1500 backBuffer->Release();
1502 mSize = aSize;
1505 void DCSwapChain::Present() {
1506 const auto gl = mDCLayerTree->GetGLContext();
1507 const auto& gle = gl::GLContextEGL::Cast(gl);
1509 HRESULT hr = mSwapChain->Present(0, 0);
1510 MOZ_RELEASE_ASSERT(SUCCEEDED(hr));
1512 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
1515 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree)
1516 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
1517 aDCLayerTree),
1518 mSwapChainBufferCount(
1519 StaticPrefs::gfx_webrender_dcomp_video_force_triple_buffering() ? 3
1520 : 2) {
1523 DCSurfaceVideo::~DCSurfaceVideo() {
1524 ReleaseDecodeSwapChainResources();
1525 MOZ_ASSERT(!mSwapChainSurfaceHandle);
1528 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) {
1529 if (aFormat == DXGI_FORMAT_NV12 || aFormat == DXGI_FORMAT_YUY2) {
1530 return true;
1532 return false;
1535 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) {
1536 auto [texture, usageInfo] =
1537 RenderThread::Get()->GetRenderTextureAndUsageInfo(aExternalImage);
1538 MOZ_RELEASE_ASSERT(texture);
1540 if (usageInfo) {
1541 mRenderTextureHostUsageInfo = usageInfo;
1544 if (mPrevTexture == texture) {
1545 return;
1548 // XXX if software decoded video frame format is nv12, it could be used as
1549 // video overlay.
1550 if (!texture || !texture->AsRenderDXGITextureHost() ||
1551 texture->GetFormat() != gfx::SurfaceFormat::NV12) {
1552 gfxCriticalNote << "Unsupported RenderTexture for overlay: "
1553 << gfx::hexa(texture);
1554 return;
1557 mRenderTextureHost = texture;
1560 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix& aTransform) {
1561 if (!mRenderTextureHost) {
1562 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1563 return false;
1566 const auto overlayType = mRenderTextureHost->IsSoftwareDecodedVideo()
1567 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1568 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO;
1569 mDCLayerTree->SetUsedOverlayTypeInFrame(overlayType);
1571 mVideoSize = mRenderTextureHost->AsRenderDXGITextureHost()->GetSize(0);
1573 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1574 // DCSurfaceVideo::PresentVideo() needs to be called.
1575 bool needsToPresent = mPrevTexture != mRenderTextureHost;
1576 gfx::IntSize swapChainSize = mVideoSize;
1577 gfx::Matrix transform = aTransform;
1578 const bool isDRM = mRenderTextureHost->IsFromDRMSource();
1580 // When video is rendered to axis aligned integer rectangle, video scaling
1581 // could be done by VideoProcessor
1582 bool scaleVideoAtVideoProcessor = false;
1583 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1584 aTransform.PreservesAxisAlignedRectangles()) {
1585 gfx::Size scaledSize = gfx::Size(mVideoSize) * aTransform.ScaleFactors();
1586 gfx::IntSize size(int32_t(std::round(scaledSize.width)),
1587 int32_t(std::round(scaledSize.height)));
1588 if (gfx::FuzzyEqual(scaledSize.width, size.width, 0.1f) &&
1589 gfx::FuzzyEqual(scaledSize.height, size.height, 0.1f)) {
1590 scaleVideoAtVideoProcessor = true;
1591 swapChainSize = size;
1595 if (scaleVideoAtVideoProcessor) {
1596 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1597 // subsampled formats like NV12 must have an even width and height.
1598 if (swapChainSize.width % 2 == 1) {
1599 swapChainSize.width += 1;
1601 if (swapChainSize.height % 2 == 1) {
1602 swapChainSize.height += 1;
1604 transform = gfx::Matrix::Translation(aTransform.GetTranslation());
1607 if (!mDCLayerTree->EnsureVideoProcessor(mVideoSize, swapChainSize)) {
1608 gfxCriticalNote << "EnsureVideoProcessor Failed";
1609 return false;
1612 MOZ_ASSERT(mDCLayerTree->GetVideoContext());
1613 MOZ_ASSERT(mDCLayerTree->GetVideoProcessor());
1615 const UINT vendorId = GetVendorId(mDCLayerTree->GetVideoDevice());
1616 const bool driverSupportsAutoHDR =
1617 GetVpAutoHDRSupported(vendorId, mDCLayerTree->GetVideoContext(),
1618 mDCLayerTree->GetVideoProcessor());
1619 const bool contentIsHDR = false; // XXX for now, only non-HDR is supported.
1620 const bool monitorIsHDR =
1621 gfx::DeviceManagerDx::Get()->WindowHDREnabled(mDCLayerTree->GetHwnd());
1622 const bool powerIsCharging = RenderThread::Get()->GetPowerIsCharging();
1624 bool useVpAutoHDR = gfx::gfxVars::WebRenderOverlayVpAutoHDR() &&
1625 !contentIsHDR && monitorIsHDR && driverSupportsAutoHDR &&
1626 powerIsCharging && !mVpAutoHDRFailed;
1628 if (profiler_thread_is_being_profiled_for_markers()) {
1629 nsPrintfCString str(
1630 "useVpAutoHDR %d gfxVars %d contentIsHDR %d monitor %d driver %d "
1631 "charging %d failed %d",
1632 useVpAutoHDR, gfx::gfxVars::WebRenderOverlayVpAutoHDR(), contentIsHDR,
1633 monitorIsHDR, driverSupportsAutoHDR, powerIsCharging, mVpAutoHDRFailed);
1634 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str);
1637 if (!mVideoSwapChain || mSwapChainSize != swapChainSize || mIsDRM != isDRM ||
1638 mUseVpAutoHDR != useVpAutoHDR) {
1639 needsToPresent = true;
1640 ReleaseDecodeSwapChainResources();
1641 // Update mSwapChainSize before creating SwapChain
1642 mSwapChainSize = swapChainSize;
1643 mIsDRM = isDRM;
1645 auto swapChainFormat = GetSwapChainFormat(useVpAutoHDR);
1646 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
1647 if (useYUVSwapChain) {
1648 // Tries to create YUV SwapChain
1649 CreateVideoSwapChain(swapChainFormat);
1650 if (!mVideoSwapChain) {
1651 mFailedYuvSwapChain = true;
1652 ReleaseDecodeSwapChainResources();
1654 gfxCriticalNote << "Fallback to RGB SwapChain";
1657 // Tries to create RGB SwapChain
1658 if (!mVideoSwapChain) {
1659 CreateVideoSwapChain(swapChainFormat);
1661 if (!mVideoSwapChain && useVpAutoHDR) {
1662 mVpAutoHDRFailed = true;
1663 gfxCriticalNoteOnce << "Failed to create video SwapChain for VpAutoHDR";
1665 // Disable VpAutoHDR
1666 useVpAutoHDR = false;
1667 swapChainFormat = GetSwapChainFormat(useVpAutoHDR);
1668 CreateVideoSwapChain(swapChainFormat);
1672 aTransform = transform;
1673 mUseVpAutoHDR = useVpAutoHDR;
1675 return needsToPresent;
1678 void DCSurfaceVideo::PresentVideo() {
1679 if (!mRenderTextureHost) {
1680 return;
1683 if (!mVideoSwapChain) {
1684 gfxCriticalNote << "Failed to create VideoSwapChain";
1685 RenderThread::Get()->NotifyWebRenderError(
1686 wr::WebRenderError::VIDEO_OVERLAY);
1687 return;
1690 mVisual->SetContent(mVideoSwapChain);
1692 if (!CallVideoProcessorBlt()) {
1693 bool useYUVSwapChain = IsYUVSwapChainFormat(mSwapChainFormat);
1694 if (useYUVSwapChain) {
1695 mFailedYuvSwapChain = true;
1696 ReleaseDecodeSwapChainResources();
1697 return;
1699 RenderThread::Get()->NotifyWebRenderError(
1700 wr::WebRenderError::VIDEO_OVERLAY);
1701 return;
1704 HRESULT hr;
1705 UINT flags = DXGI_PRESENT_USE_DURATION;
1706 UINT interval = 1;
1708 auto start = TimeStamp::Now();
1709 hr = mVideoSwapChain->Present(interval, flags);
1710 auto end = TimeStamp::Now();
1712 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
1713 gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr);
1716 mPrevTexture = mRenderTextureHost;
1718 // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops
1719 // fps.
1721 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
1722 return;
1725 const auto maxPresentWaitDurationMs = 2;
1726 const auto maxSlowPresentCount = 5;
1727 const auto presentDurationMs =
1728 static_cast<uint32_t>((end - start).ToMilliseconds());
1729 const auto overlayType = mRenderTextureHost->IsSoftwareDecodedVideo()
1730 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1731 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO;
1733 nsPrintfCString marker("PresentWait overlay %u %ums ", (uint8_t)overlayType,
1734 presentDurationMs);
1735 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS, {}, marker);
1737 if (presentDurationMs > maxPresentWaitDurationMs) {
1738 mSlowPresentCount++;
1739 } else {
1740 mSlowPresentCount = 0;
1743 if (mSlowPresentCount <= maxSlowPresentCount) {
1744 return;
1747 DisableVideoOverlay();
1749 if (overlayType == DCompOverlayTypes::SOFTWARE_DECODED_VIDEO) {
1750 gfxCriticalNoteOnce << "Sw video swapchain present is slow";
1752 nsPrintfCString marker("Sw video swapchain present is slow");
1753 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
1754 } else {
1755 gfxCriticalNoteOnce << "Hw video swapchain present is slow";
1757 nsPrintfCString marker("Hw video swapchain present is slow");
1758 PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
1762 void DCSurfaceVideo::DisableVideoOverlay() {
1763 if (!mRenderTextureHostUsageInfo) {
1764 return;
1766 mRenderTextureHostUsageInfo->DisableVideoOverlay();
1769 DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat(bool aUseVpAutoHDR) {
1770 if (aUseVpAutoHDR) {
1771 return DXGI_FORMAT_R16G16B16A16_FLOAT;
1773 if (mFailedYuvSwapChain || !mDCLayerTree->SupportsHardwareOverlays()) {
1774 return DXGI_FORMAT_B8G8R8A8_UNORM;
1776 return mDCLayerTree->GetOverlayFormatForSDR();
1779 bool DCSurfaceVideo::CreateVideoSwapChain(DXGI_FORMAT aSwapChainFormat) {
1780 MOZ_ASSERT(mRenderTextureHost);
1782 const auto device = mDCLayerTree->GetDevice();
1784 RefPtr<IDXGIDevice> dxgiDevice;
1785 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
1787 RefPtr<IDXGIFactoryMedia> dxgiFactoryMedia;
1789 RefPtr<IDXGIAdapter> adapter;
1790 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
1791 adapter->GetParent(
1792 IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia)));
1795 mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1796 if (!mSwapChainSurfaceHandle) {
1797 gfxCriticalNote << "Failed to create DCompSurfaceHandle";
1798 return false;
1801 DXGI_SWAP_CHAIN_DESC1 desc = {};
1802 desc.Width = mSwapChainSize.width;
1803 desc.Height = mSwapChainSize.height;
1804 desc.Format = aSwapChainFormat;
1805 desc.Stereo = FALSE;
1806 desc.SampleDesc.Count = 1;
1807 desc.BufferCount = mSwapChainBufferCount;
1808 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1809 desc.Scaling = DXGI_SCALING_STRETCH;
1810 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1811 desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO;
1812 if (IsYUVSwapChainFormat(aSwapChainFormat)) {
1813 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1815 if (mIsDRM) {
1816 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY;
1818 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
1820 HRESULT hr;
1821 hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(
1822 device, mSwapChainSurfaceHandle, &desc, nullptr,
1823 getter_AddRefs(mVideoSwapChain));
1825 if (FAILED(hr)) {
1826 gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr)
1827 << " " << mSwapChainSize;
1828 return false;
1831 mSwapChainFormat = aSwapChainFormat;
1832 return true;
1835 // TODO: Replace with YUVRangedColorSpace
1836 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1837 const gfx::YUVColorSpace aYUVColorSpace,
1838 const gfx::ColorRange aColorRange) {
1839 if (aYUVColorSpace == gfx::YUVColorSpace::BT601) {
1840 if (aColorRange == gfx::ColorRange::FULL) {
1841 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601);
1842 } else {
1843 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601);
1845 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) {
1846 if (aColorRange == gfx::ColorRange::FULL) {
1847 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709);
1848 } else {
1849 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709);
1851 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) {
1852 if (aColorRange == gfx::ColorRange::FULL) {
1853 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1854 // video overlay.
1855 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020);
1856 } else {
1857 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020);
1861 return Nothing();
1864 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1865 const gfx::YUVRangedColorSpace aYUVColorSpace) {
1866 const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
1867 return GetSourceDXGIColorSpace(info.space, info.range);
1870 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1871 MOZ_ASSERT(mRenderTextureHost);
1873 HRESULT hr;
1874 const auto videoDevice = mDCLayerTree->GetVideoDevice();
1875 const auto videoContext = mDCLayerTree->GetVideoContext();
1876 const auto texture = mRenderTextureHost->AsRenderDXGITextureHost();
1878 Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace =
1879 GetSourceDXGIColorSpace(texture->GetYUVColorSpace());
1880 if (sourceColorSpace.isNothing()) {
1881 gfxCriticalNote << "Unsupported color space";
1882 return false;
1885 RefPtr<ID3D11Texture2D> texture2D = texture->GetD3D11Texture2DWithGL();
1886 if (!texture2D) {
1887 gfxCriticalNote << "Failed to get D3D11Texture2D";
1888 return false;
1891 if (!mVideoSwapChain) {
1892 return false;
1895 auto query = texture->GetQuery();
1896 if (query) {
1897 // Wait ID3D11Query of D3D11Texture2D copy complete just before blitting for
1898 // video overlay with non Intel GPUs. See Bug 1817617.
1899 BOOL result;
1900 bool ret = layers::WaitForFrameGPUQuery(mDCLayerTree->GetDevice(),
1901 mDCLayerTree->GetDeviceContext(),
1902 query, &result);
1903 if (!ret) {
1904 gfxCriticalNoteOnce << "WaitForFrameGPUQuery() failed";
1908 RefPtr<IDXGISwapChain3> swapChain3;
1909 mVideoSwapChain->QueryInterface(
1910 (IDXGISwapChain3**)getter_AddRefs(swapChain3));
1911 if (!swapChain3) {
1912 gfxCriticalNote << "Failed to get IDXGISwapChain3";
1913 return false;
1916 RefPtr<ID3D11VideoContext1> videoContext1;
1917 videoContext->QueryInterface(
1918 (ID3D11VideoContext1**)getter_AddRefs(videoContext1));
1919 if (!videoContext1) {
1920 gfxCriticalNote << "Failed to get ID3D11VideoContext1";
1921 return false;
1924 const auto videoProcessor = mDCLayerTree->GetVideoProcessor();
1925 const auto videoProcessorEnumerator =
1926 mDCLayerTree->GetVideoProcessorEnumerator();
1928 DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref();
1929 videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0,
1930 inputColorSpace);
1932 DXGI_COLOR_SPACE_TYPE outputColorSpace =
1933 IsYUVSwapChainFormat(mSwapChainFormat)
1934 ? inputColorSpace
1935 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
1937 if (mUseVpAutoHDR) {
1938 outputColorSpace = mSwapChainFormat == DXGI_FORMAT_R16G16B16A16_FLOAT
1939 ? DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709
1940 : DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
1943 hr = swapChain3->SetColorSpace1(outputColorSpace);
1944 if (FAILED(hr)) {
1945 gfxCriticalNoteOnce << "SetColorSpace1 failed: " << gfx::hexa(hr);
1946 RenderThread::Get()->NotifyWebRenderError(
1947 wr::WebRenderError::VIDEO_OVERLAY);
1948 return false;
1950 videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor,
1951 outputColorSpace);
1953 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {};
1954 inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
1955 inputDesc.Texture2D.ArraySlice = texture->ArrayIndex();
1957 RefPtr<ID3D11VideoProcessorInputView> inputView;
1958 hr = videoDevice->CreateVideoProcessorInputView(
1959 texture2D, videoProcessorEnumerator, &inputDesc,
1960 getter_AddRefs(inputView));
1961 if (FAILED(hr)) {
1962 gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: "
1963 << gfx::hexa(hr);
1964 return false;
1967 D3D11_VIDEO_PROCESSOR_STREAM stream = {};
1968 stream.Enable = true;
1969 stream.OutputIndex = 0;
1970 stream.InputFrameOrField = 0;
1971 stream.PastFrames = 0;
1972 stream.FutureFrames = 0;
1973 stream.pInputSurface = inputView.get();
1975 RECT destRect;
1976 destRect.left = 0;
1977 destRect.top = 0;
1978 destRect.right = mSwapChainSize.width;
1979 destRect.bottom = mSwapChainSize.height;
1981 videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE,
1982 &destRect);
1983 videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE,
1984 &destRect);
1985 RECT sourceRect;
1986 sourceRect.left = 0;
1987 sourceRect.top = 0;
1988 sourceRect.right = mVideoSize.width;
1989 sourceRect.bottom = mVideoSize.height;
1990 videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE,
1991 &sourceRect);
1993 if (!mOutputView) {
1994 RefPtr<ID3D11Texture2D> backBuf;
1995 mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
1996 (void**)getter_AddRefs(backBuf));
1998 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {};
1999 outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
2000 outputDesc.Texture2D.MipSlice = 0;
2002 hr = videoDevice->CreateVideoProcessorOutputView(
2003 backBuf, videoProcessorEnumerator, &outputDesc,
2004 getter_AddRefs(mOutputView));
2005 if (FAILED(hr)) {
2006 gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: "
2007 << gfx::hexa(hr);
2008 return false;
2012 const UINT vendorId = GetVendorId(videoDevice);
2013 const auto powerIsCharging = RenderThread::Get()->GetPowerIsCharging();
2014 const bool useSuperResolution =
2015 gfx::gfxVars::WebRenderOverlayVpSuperResolution() && powerIsCharging &&
2016 !mVpSuperResolutionFailed;
2018 if (profiler_thread_is_being_profiled_for_markers()) {
2019 nsPrintfCString str(
2020 "useSuperResolution %d gfxVars %d charging %d failed %d",
2021 useSuperResolution, gfx::gfxVars::WebRenderOverlayVpSuperResolution(),
2022 powerIsCharging, mVpSuperResolutionFailed);
2023 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str);
2026 if (useSuperResolution) {
2027 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {},
2028 "SetVpSuperResolution"_ns);
2030 hr = SetVpSuperResolution(vendorId, videoContext, videoProcessor, true);
2031 if (FAILED(hr)) {
2032 if (hr != E_NOTIMPL) {
2033 gfxCriticalNoteOnce << "SetVpSuperResolution failed: " << gfx::hexa(hr);
2035 mVpSuperResolutionFailed = true;
2037 } else if (gfx::gfxVars::WebRenderOverlayVpSuperResolution() &&
2038 !useSuperResolution) {
2039 SetVpSuperResolution(vendorId, videoContext, videoProcessor, false);
2042 if (profiler_thread_is_being_profiled_for_markers() && vendorId == 0x10DE) {
2043 AddProfileMarkerForNvidiaVpSuperResolutionInfo(videoContext,
2044 videoProcessor);
2047 if (mUseVpAutoHDR) {
2048 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, "SetVpAutoHDR"_ns);
2050 hr = SetVpAutoHDR(vendorId, videoContext, videoProcessor, true);
2051 if (FAILED(hr)) {
2052 gfxCriticalNoteOnce << "SetVpAutoHDR failed: " << gfx::hexa(hr);
2053 mVpAutoHDRFailed = true;
2057 hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1,
2058 &stream);
2059 if (FAILED(hr)) {
2060 gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr);
2061 return false;
2064 return true;
2067 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
2068 mOutputView = nullptr;
2069 mVideoSwapChain = nullptr;
2070 mDecodeSwapChain = nullptr;
2071 mDecodeResource = nullptr;
2072 if (mSwapChainSurfaceHandle) {
2073 ::CloseHandle(mSwapChainSurfaceHandle);
2074 mSwapChainSurfaceHandle = 0;
2076 mUseVpAutoHDR = false;
2079 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree)
2080 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
2081 aDCLayerTree) {}
2083 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage) {
2084 RenderTextureHost* texture =
2085 RenderThread::Get()->GetRenderTexture(aExternalImage);
2086 RenderDcompSurfaceTextureHost* renderTexture =
2087 texture ? texture->AsRenderDcompSurfaceTextureHost() : nullptr;
2088 if (!renderTexture) {
2089 gfxCriticalNote << "Unsupported RenderTexture for DCSurfaceHandle: "
2090 << gfx::hexa(texture);
2091 return;
2094 const auto handle = renderTexture->GetDcompSurfaceHandle();
2095 if (GetSurfaceHandle() == handle) {
2096 return;
2099 LOG_H("AttachExternalImage, ext-image=%" PRIu64 ", texture=%p, handle=%p",
2100 wr::AsUint64(aExternalImage), renderTexture, handle);
2101 mDcompTextureHost = renderTexture;
2104 HANDLE DCSurfaceHandle::GetSurfaceHandle() const {
2105 if (mDcompTextureHost) {
2106 return mDcompTextureHost->GetDcompSurfaceHandle();
2108 return nullptr;
2111 IDCompositionSurface* DCSurfaceHandle::EnsureSurface() {
2112 if (auto* surface = mDcompTextureHost->GetSurface()) {
2113 return surface;
2116 // Texture host hasn't created the surface yet, ask it to create a new one.
2117 RefPtr<IDCompositionDevice> device;
2118 HRESULT hr = mDCLayerTree->GetCompositionDevice()->QueryInterface(
2119 (IDCompositionDevice**)getter_AddRefs(device));
2120 if (FAILED(hr)) {
2121 gfxCriticalNote
2122 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
2123 << gfx::hexa(hr);
2124 return nullptr;
2127 return mDcompTextureHost->CreateSurfaceFromDevice(device);
2130 void DCSurfaceHandle::PresentSurfaceHandle() {
2131 LOG_H("PresentSurfaceHandle");
2132 if (IDCompositionSurface* surface = EnsureSurface()) {
2133 LOG_H("Set surface %p to visual", surface);
2134 mVisual->SetContent(surface);
2135 } else {
2136 mVisual->SetContent(nullptr);
2140 DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {}
2142 DCTile::~DCTile() {}
2144 bool DCTile::Initialize(int aX, int aY, wr::DeviceIntSize aSize,
2145 bool aIsVirtualSurface, bool aIsOpaque,
2146 RefPtr<IDCompositionVisual2> mSurfaceVisual) {
2147 if (aSize.width <= 0 || aSize.height <= 0) {
2148 return false;
2151 mSize = aSize;
2152 mIsOpaque = aIsOpaque;
2153 mIsVirtualSurface = aIsVirtualSurface;
2154 mNeedsFullDraw = !aIsVirtualSurface;
2156 if (aIsVirtualSurface) {
2157 // Initially, the entire tile is considered valid, unless it is set by
2158 // the SetTileProperties method.
2159 mValidRect.x = 0;
2160 mValidRect.y = 0;
2161 mValidRect.width = aSize.width;
2162 mValidRect.height = aSize.height;
2163 } else {
2164 HRESULT hr;
2165 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
2166 // Create the visual and put it in the tree under the surface visual
2167 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
2168 if (FAILED(hr)) {
2169 gfxCriticalNote << "Failed to CreateVisual for DCTile: " << gfx::hexa(hr);
2170 return false;
2172 mSurfaceVisual->AddVisual(mVisual, false, nullptr);
2173 // Position the tile relative to the surface visual
2174 mVisual->SetOffsetX(aX * aSize.width);
2175 mVisual->SetOffsetY(aY * aSize.height);
2176 // Clip the visual so it doesn't show anything until we update it
2177 D2D_RECT_F clip = {0, 0, 0, 0};
2178 mVisual->SetClip(clip);
2179 // Create the underlying pixel buffer.
2180 mCompositionSurface = CreateCompositionSurface(aSize, aIsOpaque);
2181 if (!mCompositionSurface) {
2182 return false;
2184 hr = mVisual->SetContent(mCompositionSurface);
2185 if (FAILED(hr)) {
2186 gfxCriticalNote << "Failed to SetContent for DCTile: " << gfx::hexa(hr);
2187 return false;
2191 return true;
2194 RefPtr<IDCompositionSurface> DCTile::CreateCompositionSurface(
2195 wr::DeviceIntSize aSize, bool aIsOpaque) {
2196 HRESULT hr;
2197 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
2198 const auto alphaMode =
2199 aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
2200 RefPtr<IDCompositionSurface> compositionSurface;
2202 hr = dCompDevice->CreateSurface(aSize.width, aSize.height,
2203 DXGI_FORMAT_R8G8B8A8_UNORM, alphaMode,
2204 getter_AddRefs(compositionSurface));
2205 if (FAILED(hr)) {
2206 gfxCriticalNote << "Failed to CreateSurface for DCTile: " << gfx::hexa(hr);
2207 return nullptr;
2209 return compositionSurface;
2212 RefPtr<IDCompositionSurface> DCTile::Bind(wr::DeviceIntRect aValidRect) {
2213 if (mVisual != nullptr) {
2214 // Tile owns a visual, set the size of the visual to match the portion we
2215 // want to be visible.
2216 D2D_RECT_F clip_rect;
2217 clip_rect.left = aValidRect.min.x;
2218 clip_rect.top = aValidRect.min.y;
2219 clip_rect.right = aValidRect.max.x;
2220 clip_rect.bottom = aValidRect.max.y;
2221 mVisual->SetClip(clip_rect);
2223 return mCompositionSurface;
2226 GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface(
2227 wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
2228 RefPtr<IDCompositionSurface> aCompositionSurface,
2229 wr::DeviceIntPoint aSurfaceOffset) {
2230 MOZ_ASSERT(aCompositionSurface.get());
2232 HRESULT hr;
2233 const auto gl = GetGLContext();
2234 RefPtr<ID3D11Texture2D> backBuf;
2235 POINT offset;
2237 RECT update_rect;
2238 update_rect.left = aSurfaceOffset.x + aDirtyRect.min.x;
2239 update_rect.top = aSurfaceOffset.y + aDirtyRect.min.y;
2240 update_rect.right = aSurfaceOffset.x + aDirtyRect.max.x;
2241 update_rect.bottom = aSurfaceOffset.y + aDirtyRect.max.y;
2242 hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
2243 (void**)getter_AddRefs(backBuf), &offset);
2245 if (FAILED(hr)) {
2246 LayoutDeviceIntRect rect = widget::WinUtils::ToIntRect(update_rect);
2248 gfxCriticalNote << "DCompositionSurface::BeginDraw failed: "
2249 << gfx::hexa(hr) << " " << rect;
2250 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW);
2251 return false;
2254 // DC includes the origin of the dirty / update rect in the draw offset,
2255 // undo that here since WR expects it to be an absolute offset.
2256 offset.x -= aDirtyRect.min.x;
2257 offset.y -= aDirtyRect.min.y;
2259 D3D11_TEXTURE2D_DESC desc;
2260 backBuf->GetDesc(&desc);
2262 const auto& gle = gl::GLContextEGL::Cast(gl);
2263 const auto& egl = gle->mEgl;
2265 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
2267 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
2268 const EGLint attribs[] = {LOCAL_EGL_NONE};
2269 mEGLImage = egl->fCreateImage(EGL_NO_CONTEXT, LOCAL_EGL_D3D11_TEXTURE_ANGLE,
2270 buffer, attribs);
2272 // Get the current FBO and RBO id, so we can restore them later
2273 GLint currentFboId, currentRboId;
2274 gl->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
2275 gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
2277 // Create a render buffer object that is backed by the EGL image.
2278 gl->fGenRenderbuffers(1, &mColorRBO);
2279 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO);
2280 gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage);
2282 // Get or create an FBO for the specified dimensions
2283 GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height);
2285 // Attach the new renderbuffer to the FBO
2286 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
2287 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
2288 LOCAL_GL_COLOR_ATTACHMENT0,
2289 LOCAL_GL_RENDERBUFFER, mColorRBO);
2291 // Restore previous FBO and RBO bindings
2292 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, currentFboId);
2293 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId);
2295 aOffset->x = offset.x;
2296 aOffset->y = offset.y;
2298 return fboId;
2301 void DCLayerTree::DestroyEGLSurface() {
2302 const auto gl = GetGLContext();
2304 if (mColorRBO) {
2305 gl->fDeleteRenderbuffers(1, &mColorRBO);
2306 mColorRBO = 0;
2309 if (mEGLImage) {
2310 const auto& gle = gl::GLContextEGL::Cast(gl);
2311 const auto& egl = gle->mEgl;
2312 egl->fDestroyImage(mEGLImage);
2313 mEGLImage = EGL_NO_IMAGE;
2317 // -
2319 } // namespace wr
2320 namespace gfx {
2322 color::ColorProfileDesc QueryOutputColorProfile() {
2323 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
2324 // but we do need gfxPlatform::GetCMSOutputProfile().
2325 // So we steal what we need through the window:
2326 const auto outputProfileData =
2327 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
2329 const auto qcmsProfile = qcms_profile_from_memory(
2330 outputProfileData.Elements(), outputProfileData.Length());
2331 const auto release = MakeScopeExit([&]() {
2332 if (qcmsProfile) {
2333 qcms_profile_release(qcmsProfile);
2337 const bool print = gfxEnv::MOZ_GL_SPEW();
2339 const auto ret = [&]() {
2340 if (qcmsProfile) {
2341 return color::ColorProfileDesc::From(*qcmsProfile);
2343 if (print) {
2344 printf_stderr(
2345 "Missing or failed to load display color profile, defaulting to "
2346 "sRGB.\n");
2348 const auto MISSING_PROFILE_DEFAULT_SPACE = color::ColorspaceDesc{
2349 color::Chromaticities::Srgb(),
2350 color::PiecewiseGammaDesc::Srgb(),
2352 return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE);
2353 }();
2355 if (print) {
2356 const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r);
2357 printf_stderr(
2358 "Display profile:\n"
2359 " Approx Gamma: %f\n"
2360 " XYZ-D65 Red : %f, %f, %f\n"
2361 " XYZ-D65 Green: %f, %f, %f\n"
2362 " XYZ-D65 Blue : %f, %f, %f\n",
2363 gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0),
2364 ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2),
2366 ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1),
2367 ret.xyzd65FromLinearRgb.at(1, 2),
2369 ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1),
2370 ret.xyzd65FromLinearRgb.at(2, 2));
2373 return ret;
2376 } // namespace gfx
2377 namespace wr {
2379 inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) {
2380 return D2D1_MATRIX_5X4_F{{{
2381 m.rows[0][0],
2382 m.rows[1][0],
2383 m.rows[2][0],
2384 m.rows[3][0],
2385 m.rows[0][1],
2386 m.rows[1][1],
2387 m.rows[2][1],
2388 m.rows[3][1],
2389 m.rows[0][2],
2390 m.rows[1][2],
2391 m.rows[2][2],
2392 m.rows[3][2],
2393 m.rows[0][3],
2394 m.rows[1][3],
2395 m.rows[2][3],
2396 m.rows[3][3],
2401 }}};
2404 ColorManagementChain ColorManagementChain::From(
2405 IDCompositionDevice3& dcomp,
2406 const color::ColorProfileConversionDesc& conv) {
2407 auto ret = ColorManagementChain{};
2409 const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) {
2410 if (ret.last) {
2411 afterLast->SetInput(0, ret.last, 0);
2413 ret.last = afterLast;
2416 const auto MaybeAppendColorMatrix = [&](const color::mat4& m) {
2417 RefPtr<IDCompositionColorMatrixEffect> e;
2418 if (approx(m, color::mat4::Identity())) return e;
2419 dcomp.CreateColorMatrixEffect(getter_AddRefs(e));
2420 MOZ_ASSERT(e);
2421 if (!e) return e;
2422 e->SetMatrix(to_D2D1_MATRIX_5X4_F(m));
2423 Append(e);
2424 return e;
2426 const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) {
2427 RefPtr<IDCompositionTableTransferEffect> e;
2428 if (!t.r.size() && !t.g.size() && !t.b.size()) return e;
2429 dcomp.CreateTableTransferEffect(getter_AddRefs(e));
2430 MOZ_ASSERT(e);
2431 if (!e) return e;
2432 e->SetRedTable(t.r.data(), t.r.size());
2433 e->SetGreenTable(t.g.data(), t.g.size());
2434 e->SetBlueTable(t.b.data(), t.b.size());
2435 Append(e);
2436 return e;
2439 ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv);
2440 ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf);
2441 ret.dstLinearFromSrcLinear =
2442 MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear));
2443 ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear);
2445 return ret;
2448 ColorManagementChain::~ColorManagementChain() = default;
2450 } // namespace wr
2451 } // namespace mozilla
2453 #undef LOG_H