DCLayerTree.cpp (112562B)
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/. */ 6 7 #include "DCLayerTree.h" 8 9 // - 10 11 #include <d3d11.h> 12 #include <dcomp.h> 13 #include <d3d11_1.h> 14 #include <dxgi1_2.h> 15 16 // - 17 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/gfx/StackArray.h" 27 #include "mozilla/layers/CompositeProcessD3D11FencesHolderMap.h" 28 #include "mozilla/StaticPrefs_gfx.h" 29 #include "mozilla/StaticPtr.h" 30 #include "mozilla/webrender/RenderD3D11TextureHost.h" 31 #include "mozilla/webrender/RenderDcompSurfaceTextureHost.h" 32 #include "mozilla/webrender/RenderTextureHost.h" 33 #include "mozilla/webrender/RenderThread.h" 34 #include "mozilla/WindowsVersion.h" 35 #include "mozilla/glean/GfxMetrics.h" 36 #include "nsPrintfCString.h" 37 #include "WinUtils.h" 38 39 // - 40 41 namespace mozilla { 42 namespace wr { 43 44 extern LazyLogModule gRenderThreadLog; 45 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__)) 46 47 #define LOG_H(msg, ...) \ 48 MOZ_LOG(gDcompSurface, LogLevel::Debug, \ 49 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__)) 50 51 static UINT GetVendorId(ID3D11VideoDevice* const aVideoDevice) { 52 RefPtr<IDXGIDevice> dxgiDevice; 53 RefPtr<IDXGIAdapter> adapter; 54 aVideoDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); 55 dxgiDevice->GetAdapter(getter_AddRefs(adapter)); 56 57 DXGI_ADAPTER_DESC adapterDesc; 58 adapter->GetDesc(&adapterDesc); 59 60 return adapterDesc.VendorId; 61 } 62 63 // Undocumented NVIDIA VSR data 64 struct NvidiaVSRGetData_v1 { 65 UINT vsrGPUisVSRCapable : 1; // 01/32, 1: GPU is VSR capable 66 UINT vsrOtherFieldsValid : 1; // 02/32, 1: Other status fields are valid 67 // remaining fields are valid if vsrOtherFieldsValid is set - requires 68 // previous execution of VPBlt with SetStreamExtension for VSR enabled. 69 UINT vsrEnabled : 1; // 03/32, 1: VSR is enabled 70 UINT vsrIsInUseForThisVP : 1; // 04/32, 1: VSR is in use by this Video 71 // Processor 72 UINT vsrLevel : 3; // 05-07/32, 0-4 current level 73 UINT vsrReserved : 21; // 32-07 74 }; 75 76 static Result<NvidiaVSRGetData_v1, HRESULT> GetNvidiaVpSuperResolutionInfo( 77 ID3D11VideoContext* aVideoContext, ID3D11VideoProcessor* aVideoProcessor) { 78 MOZ_ASSERT(aVideoContext); 79 MOZ_ASSERT(aVideoProcessor); 80 81 // Undocumented NVIDIA driver constants 82 constexpr GUID nvGUID = {0xD43CE1B3, 83 0x1F4B, 84 0x48AC, 85 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}}; 86 87 NvidiaVSRGetData_v1 data{}; 88 HRESULT hr = aVideoContext->VideoProcessorGetStreamExtension( 89 aVideoProcessor, 0, &nvGUID, sizeof(data), &data); 90 91 if (FAILED(hr)) { 92 return Err(hr); 93 } 94 return data; 95 } 96 97 static void AddProfileMarkerForNvidiaVpSuperResolutionInfo( 98 ID3D11VideoContext* aVideoContext, ID3D11VideoProcessor* aVideoProcessor) { 99 MOZ_ASSERT(profiler_thread_is_being_profiled_for_markers()); 100 101 auto res = GetNvidiaVpSuperResolutionInfo(aVideoContext, aVideoProcessor); 102 if (res.isErr()) { 103 return; 104 } 105 106 auto data = res.unwrap(); 107 108 nsPrintfCString str( 109 "SuperResolution VP Capable %u OtherFieldsValid %u Enabled %u InUse %u " 110 "Level %u", 111 data.vsrGPUisVSRCapable, data.vsrOtherFieldsValid, data.vsrEnabled, 112 data.vsrIsInUseForThisVP, data.vsrLevel); 113 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str); 114 } 115 116 static HRESULT SetNvidiaVpSuperResolution(ID3D11VideoContext* aVideoContext, 117 ID3D11VideoProcessor* aVideoProcessor, 118 bool aEnable) { 119 LOG("SetNvidiaVpSuperResolution() aEnable=%d", aEnable); 120 121 // Undocumented NVIDIA driver constants 122 constexpr GUID nvGUID = {0xD43CE1B3, 123 0x1F4B, 124 0x48AC, 125 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}}; 126 127 constexpr UINT nvExtensionVersion = 0x1; 128 constexpr UINT nvExtensionMethodSuperResolution = 0x2; 129 struct { 130 UINT version; 131 UINT method; 132 UINT enable; 133 } streamExtensionInfo = {nvExtensionVersion, nvExtensionMethodSuperResolution, 134 aEnable ? 1u : 0}; 135 136 HRESULT hr; 137 hr = aVideoContext->VideoProcessorSetStreamExtension( 138 aVideoProcessor, 0, &nvGUID, sizeof(streamExtensionInfo), 139 &streamExtensionInfo); 140 return hr; 141 } 142 143 static HRESULT SetVpSuperResolution(UINT aGpuVendorId, 144 ID3D11VideoContext* aVideoContext, 145 ID3D11VideoProcessor* aVideoProcessor, 146 bool aEnable) { 147 MOZ_ASSERT(aVideoContext); 148 MOZ_ASSERT(aVideoProcessor); 149 150 if (aGpuVendorId == 0x10DE) { 151 return SetNvidiaVpSuperResolution(aVideoContext, aVideoProcessor, aEnable); 152 } 153 return E_NOTIMPL; 154 } 155 156 static bool GetNvidiaRTXVideoTrueHDRSupported( 157 ID3D11VideoContext* aVideoContext, ID3D11VideoProcessor* aVideoProcessor) { 158 const GUID kNvidiaTrueHDRInterfaceGUID = { 159 0xfdd62bb4, 160 0x620b, 161 0x4fd7, 162 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}}; 163 UINT available = 0; 164 HRESULT hr = aVideoContext->VideoProcessorGetStreamExtension( 165 aVideoProcessor, 0, &kNvidiaTrueHDRInterfaceGUID, sizeof(available), 166 &available); 167 if (FAILED(hr)) { 168 return false; 169 } 170 171 bool driverSupportsTrueHdr = (available == 1); 172 return driverSupportsTrueHdr; 173 } 174 175 static HRESULT SetNvidiaRTXVideoTrueHDR(ID3D11VideoContext* aVideoContext, 176 ID3D11VideoProcessor* aVideoProcessor, 177 bool aEnable) { 178 constexpr GUID kNvidiaTrueHDRInterfaceGUID = { 179 0xfdd62bb4, 180 0x620b, 181 0x4fd7, 182 {0x9a, 0xb3, 0x1e, 0x59, 0xd0, 0xd5, 0x44, 0xb3}}; 183 constexpr UINT kStreamExtensionMethodTrueHDR = 0x3; 184 const UINT TrueHDRVersion4 = 4; 185 struct { 186 UINT version; 187 UINT method; 188 UINT enable : 1; 189 UINT reserved : 31; 190 } streamExtensionInfo = {TrueHDRVersion4, kStreamExtensionMethodTrueHDR, 191 aEnable ? 1u : 0u, 0u}; 192 HRESULT hr = aVideoContext->VideoProcessorSetStreamExtension( 193 aVideoProcessor, 0, &kNvidiaTrueHDRInterfaceGUID, 194 sizeof(streamExtensionInfo), &streamExtensionInfo); 195 return hr; 196 } 197 198 static bool GetVpAutoHDRSupported(UINT aGpuVendorId, 199 ID3D11VideoContext* aVideoContext, 200 ID3D11VideoProcessor* aVideoProcessor) { 201 MOZ_ASSERT(aVideoContext); 202 MOZ_ASSERT(aVideoProcessor); 203 204 if (aGpuVendorId == 0x10DE) { 205 return GetNvidiaRTXVideoTrueHDRSupported(aVideoContext, aVideoProcessor); 206 } 207 return false; 208 } 209 210 static HRESULT SetVpAutoHDR(UINT aGpuVendorId, 211 ID3D11VideoContext* aVideoContext, 212 ID3D11VideoProcessor* aVideoProcessor, 213 bool aEnable) { 214 MOZ_ASSERT(aVideoContext); 215 MOZ_ASSERT(aVideoProcessor); 216 217 if (aGpuVendorId == 0x10DE) { 218 return SetNvidiaRTXVideoTrueHDR(aVideoContext, aVideoProcessor, aEnable); 219 } 220 MOZ_ASSERT_UNREACHABLE("Unexpected to be called"); 221 return E_NOTIMPL; 222 } 223 224 StaticAutoPtr<GpuOverlayInfo> DCLayerTree::sGpuOverlayInfo; 225 226 /* static */ 227 UniquePtr<DCLayerTree> DCLayerTree::Create(gl::GLContext* aGL, 228 EGLConfig aEGLConfig, 229 ID3D11Device* aDevice, 230 ID3D11DeviceContext* aCtx, 231 HWND aHwnd, nsACString& aError) { 232 RefPtr<IDCompositionDevice2> dCompDevice = 233 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice(); 234 if (!dCompDevice) { 235 aError.Assign("DCLayerTree(no device)"_ns); 236 return nullptr; 237 } 238 239 auto layerTree = MakeUnique<DCLayerTree>(aGL, aEGLConfig, aDevice, aCtx, 240 aHwnd, dCompDevice); 241 if (!layerTree->Initialize(aHwnd, aError)) { 242 return nullptr; 243 } 244 245 return layerTree; 246 } 247 248 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo = nullptr; } 249 250 DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig, 251 ID3D11Device* aDevice, ID3D11DeviceContext* aCtx, 252 HWND aHwnd, IDCompositionDevice2* aCompositionDevice) 253 : mGL(aGL), 254 mEGLConfig(aEGLConfig), 255 mDevice(aDevice), 256 mCtx(aCtx), 257 mHwnd(aHwnd), 258 mCompositionDevice(aCompositionDevice), 259 mDebugCounter(false), 260 mDebugVisualRedrawRegions(false), 261 mEGLImage(EGL_NO_IMAGE), 262 mColorRBO(0), 263 mPendingCommit(false) { 264 LOG("DCLayerTree::DCLayerTree()"); 265 } 266 267 DCLayerTree::~DCLayerTree() { 268 LOG("DCLayerTree::~DCLayerTree()"); 269 270 ReleaseNativeCompositorResources(); 271 } 272 273 void DCLayerTree::ReleaseNativeCompositorResources() { 274 const auto gl = GetGLContext(); 275 276 DestroyEGLSurface(); 277 278 // Delete any cached FBO objects 279 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) { 280 gl->fDeleteRenderbuffers(1, &it->depthRboId); 281 gl->fDeleteFramebuffers(1, &it->fboId); 282 } 283 } 284 285 bool DCLayerTree::Initialize(HWND aHwnd, nsACString& aError) { 286 HRESULT hr; 287 288 RefPtr<IDCompositionDesktopDevice> desktopDevice; 289 hr = mCompositionDevice->QueryInterface( 290 (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice)); 291 if (FAILED(hr)) { 292 aError.Assign(nsPrintfCString( 293 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr)); 294 return false; 295 } 296 297 hr = desktopDevice->CreateTargetForHwnd(aHwnd, TRUE, 298 getter_AddRefs(mCompositionTarget)); 299 if (FAILED(hr)) { 300 aError.Assign(nsPrintfCString( 301 "DCLayerTree(create DCompositionTarget failed %lx)", hr)); 302 return false; 303 } 304 305 hr = mCompositionDevice->CreateVisual(getter_AddRefs(mRootVisual)); 306 if (FAILED(hr)) { 307 aError.Assign(nsPrintfCString( 308 "DCLayerTree(create root DCompositionVisual failed %lx)", hr)); 309 return false; 310 } 311 312 hr = 313 mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual)); 314 if (FAILED(hr)) { 315 aError.Assign(nsPrintfCString( 316 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr)); 317 return false; 318 } 319 320 if (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin() || 321 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) { 322 if (!InitializeVideoOverlaySupport()) { 323 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY); 324 } 325 } 326 if (!sGpuOverlayInfo) { 327 // Set default if sGpuOverlayInfo was not set. 328 sGpuOverlayInfo = new GpuOverlayInfo(); 329 } 330 331 // Initialize SwapChainInfo 332 SupportsSwapChainTearing(); 333 334 mCompositionTarget->SetRoot(mRootVisual); 335 // Set interporation mode to nearest, to ensure 1:1 sampling. 336 // By default, a visual inherits the interpolation mode of the parent visual. 337 // If no visuals set the interpolation mode, the default for the entire visual 338 // tree is nearest neighbor interpolation. 339 mRootVisual->SetBitmapInterpolationMode( 340 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); 341 return true; 342 } 343 344 bool FlagsSupportsOverlays(UINT flags) { 345 return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT | 346 DXGI_OVERLAY_SUPPORT_FLAG_SCALING)); 347 } 348 349 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport() 350 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat, 351 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace, 352 RefPtr<IDXGIOutput> aOutput, 353 RefPtr<ID3D11Device> aD3d11Device) { 354 UINT colorSpaceSupportFlags = 0; 355 RefPtr<IDXGIOutput4> output4; 356 357 if (FAILED(aOutput->QueryInterface(__uuidof(IDXGIOutput4), 358 getter_AddRefs(output4)))) { 359 return false; 360 } 361 362 if (FAILED(output4->CheckOverlayColorSpaceSupport( 363 aDxgiFormat, aDxgiColorSpace, aD3d11Device, 364 &colorSpaceSupportFlags))) { 365 return false; 366 } 367 368 return (colorSpaceSupportFlags & 369 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT); 370 } 371 372 bool DCLayerTree::InitializeVideoOverlaySupport() { 373 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater()); 374 375 HRESULT hr; 376 377 hr = mDevice->QueryInterface( 378 (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice)); 379 if (FAILED(hr)) { 380 gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr); 381 return false; 382 } 383 384 hr = 385 mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext)); 386 if (FAILED(hr)) { 387 gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr); 388 return false; 389 } 390 391 if (sGpuOverlayInfo) { 392 return true; 393 } 394 395 UniquePtr<GpuOverlayInfo> info = MakeUnique<GpuOverlayInfo>(); 396 397 RefPtr<IDXGIDevice> dxgiDevice; 398 RefPtr<IDXGIAdapter> adapter; 399 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); 400 dxgiDevice->GetAdapter(getter_AddRefs(adapter)); 401 402 unsigned int i = 0; 403 while (true) { 404 RefPtr<IDXGIOutput> output; 405 if (FAILED(adapter->EnumOutputs(i++, getter_AddRefs(output)))) { 406 break; 407 } 408 RefPtr<IDXGIOutput3> output3; 409 if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput3), 410 getter_AddRefs(output3)))) { 411 break; 412 } 413 414 output3->CheckOverlaySupport(DXGI_FORMAT_NV12, mDevice, 415 &info->mNv12OverlaySupportFlags); 416 output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, mDevice, 417 &info->mYuy2OverlaySupportFlags); 418 output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, mDevice, 419 &info->mBgra8OverlaySupportFlags); 420 output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM, mDevice, 421 &info->mRgb10a2OverlaySupportFlags); 422 output3->CheckOverlaySupport(DXGI_FORMAT_R16G16B16A16_FLOAT, mDevice, 423 &info->mRgba16fOverlaySupportFlags); 424 425 if (FlagsSupportsOverlays(info->mRgb10a2OverlaySupportFlags)) { 426 info->mSupportsHDR = true; 427 } 428 429 if (FlagsSupportsOverlays(info->mRgba16fOverlaySupportFlags)) { 430 info->mSupportsHDR = true; 431 } 432 433 if (!info->mSupportsHardwareOverlays && 434 FlagsSupportsOverlays(info->mNv12OverlaySupportFlags)) { 435 // NV12 format is preferred if it's supported. 436 info->mOverlayFormatUsed = DXGI_FORMAT_NV12; 437 info->mSupportsHardwareOverlays = true; 438 } 439 440 if (!info->mSupportsHardwareOverlays && 441 FlagsSupportsOverlays(info->mYuy2OverlaySupportFlags)) { 442 // If NV12 isn't supported, fallback to YUY2 if it's supported. 443 info->mOverlayFormatUsed = DXGI_FORMAT_YUY2; 444 info->mSupportsHardwareOverlays = true; 445 } 446 447 // Early out after the first output that reports overlay support. All 448 // outputs are expected to report the same overlay support according to 449 // Microsoft's WDDM documentation: 450 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements 451 if (info->mSupportsHardwareOverlays) { 452 break; 453 } 454 } 455 456 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) { 457 info->mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM; 458 info->mSupportsHardwareOverlays = false; 459 } 460 461 info->mSupportsOverlays = info->mSupportsHardwareOverlays; 462 463 // Check VpSuperResolution and VpAutoHDR support. 464 const auto size = gfx::IntSize(100, 100); 465 if (EnsureVideoProcessor(size, size)) { 466 const UINT vendorId = GetVendorId(mVideoDevice); 467 if (vendorId == 0x10DE) { 468 auto res = GetNvidiaVpSuperResolutionInfo(mVideoContext, mVideoProcessor); 469 if (res.isOk() && res.unwrap().vsrGPUisVSRCapable) { 470 info->mSupportsVpSuperResolution = true; 471 } 472 } 473 474 const bool driverSupportVpAutoHDR = 475 GetVpAutoHDRSupported(vendorId, mVideoContext, mVideoProcessor); 476 if (driverSupportVpAutoHDR) { 477 info->mSupportsVpAutoHDR = true; 478 } 479 } 480 481 // Note: "UniquePtr::release" here is saying "release your ownership stake 482 // on your pointer, so that our StaticAutoPtr can take over ownership". 483 // (StaticAutoPtr doesn't have a move constructor that could directly steal 484 // the contents of a UniquePtr via std::move().) 485 sGpuOverlayInfo = info.release(); 486 487 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) { 488 gpuParent->NotifyOverlayInfo(GetOverlayInfo()); 489 } 490 491 return true; 492 } 493 494 DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const { 495 auto surface_it = mDCSurfaces.find(aId); 496 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); 497 return surface_it->second.get(); 498 } 499 500 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) { 501 LOG("DCLayerTree::SetDefaultSwapChain()"); 502 503 mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr); 504 mDefaultSwapChainVisual->SetContent(aSwapChain); 505 // Default SwapChain's visual does not need linear interporation. 506 mDefaultSwapChainVisual->SetBitmapInterpolationMode( 507 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); 508 mPendingCommit = true; 509 } 510 511 void DCLayerTree::MaybeUpdateDebug() { 512 bool updated = false; 513 updated |= MaybeUpdateDebugCounter(); 514 updated |= MaybeUpdateDebugVisualRedrawRegions(); 515 if (updated) { 516 mPendingCommit = true; 517 } 518 } 519 520 void DCLayerTree::MaybeCommit() { 521 if (!mPendingCommit) { 522 return; 523 } 524 mCompositionDevice->Commit(); 525 mPendingCommit = false; 526 } 527 528 void DCLayerTree::WaitForCommitCompletion() { 529 // To ensure that swapchain layers have presented to the screen 530 // for capture, call present twice. This is less than ideal, but 531 // I'm not sure if there is a better way to ensure this syncs 532 // correctly that works on both Win10/11. Even though this can 533 // be slower than necessary, it's only used by the reftest 534 // screenshotting code, so isn't particularly perf sensitive. 535 bool needsWait = false; 536 for (auto it = mDCSurfaces.begin(); it != mDCSurfaces.end(); it++) { 537 auto* surface = it->second->AsDCSwapChain(); 538 if (surface) { 539 needsWait = true; 540 } 541 } 542 543 if (needsWait) { 544 RefPtr<IDXGIDevice2> dxgiDevice2; 545 mDevice->QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice2)); 546 MOZ_ASSERT(dxgiDevice2); 547 548 HANDLE event = ::CreateEvent(nullptr, false, false, nullptr); 549 HRESULT hr = dxgiDevice2->EnqueueSetEvent(event); 550 if (SUCCEEDED(hr)) { 551 DebugOnly<DWORD> result = ::WaitForSingleObject(event, INFINITE); 552 MOZ_ASSERT(result == WAIT_OBJECT_0); 553 } else { 554 gfxCriticalNoteOnce << "EnqueueSetEvent failed: " << gfx::hexa(hr); 555 } 556 ::CloseHandle(event); 557 } 558 559 mCompositionDevice->WaitForCommitCompletion(); 560 } 561 562 bool DCLayerTree::UseNativeCompositor() const { 563 return mUseNativeCompositor && gfx::gfxVars::UseWebRenderCompositor(); 564 } 565 566 bool DCLayerTree::UseLayerCompositor() const { 567 return UseNativeCompositor() && 568 StaticPrefs::gfx_webrender_layer_compositor_AtStartup(); 569 } 570 571 void DCLayerTree::DisableNativeCompositor() { 572 MOZ_ASSERT(mCurrentSurface.isNothing()); 573 MOZ_ASSERT(mCurrentLayers.empty()); 574 575 mUseNativeCompositor = false; 576 ReleaseNativeCompositorResources(); 577 mPrevLayers.clear(); 578 mRootVisual->RemoveAllVisuals(); 579 } 580 581 bool DCLayerTree::EnableAsyncScreenshot() { 582 MOZ_ASSERT(UseLayerCompositor()); 583 if (!UseLayerCompositor()) { 584 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 585 return false; 586 } 587 588 mAsyncScreenshotLastFrameUsed = mCurrentFrame; 589 590 if (!mEnableAsyncScreenshot) { 591 mEnableAsyncScreenshotInNextFrame = true; 592 return false; 593 } 594 595 return true; 596 } 597 598 bool DCLayerTree::MaybeUpdateDebugCounter() { 599 bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter(); 600 if (mDebugCounter == debugCounter) { 601 return false; 602 } 603 604 RefPtr<IDCompositionDeviceDebug> debugDevice; 605 HRESULT hr = mCompositionDevice->QueryInterface( 606 (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice)); 607 if (FAILED(hr)) { 608 return false; 609 } 610 611 if (debugCounter) { 612 debugDevice->EnableDebugCounters(); 613 } else { 614 debugDevice->DisableDebugCounters(); 615 } 616 617 mDebugCounter = debugCounter; 618 return true; 619 } 620 621 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() { 622 bool debugVisualRedrawRegions = 623 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions(); 624 if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) { 625 return false; 626 } 627 628 RefPtr<IDCompositionVisualDebug> visualDebug; 629 HRESULT hr = mRootVisual->QueryInterface( 630 (IDCompositionVisualDebug**)getter_AddRefs(visualDebug)); 631 if (FAILED(hr)) { 632 return false; 633 } 634 635 if (debugVisualRedrawRegions) { 636 visualDebug->EnableRedrawRegions(); 637 } else { 638 visualDebug->DisableRedrawRegions(); 639 } 640 641 mDebugVisualRedrawRegions = debugVisualRedrawRegions; 642 return true; 643 } 644 645 void DCLayerTree::CompositorBeginFrame() { 646 mCurrentFrame++; 647 mUsedOverlayTypesInFrame = DCompOverlayTypes::NO_OVERLAY; 648 if (mEnableAsyncScreenshotInNextFrame) { 649 mEnableAsyncScreenshot = true; 650 mEnableAsyncScreenshotInNextFrame = false; 651 } 652 } 653 654 void DCLayerTree::CompositorEndFrame() { 655 auto start = TimeStamp::Now(); 656 // Check if the visual tree of surfaces is the same as last frame. 657 const bool same = mPrevLayers == mCurrentLayers; 658 659 if (!same) { 660 // If not, we need to rebuild the visual tree. Note that addition or 661 // removal of tiles no longer needs to rebuild the main visual tree 662 // here, since they are added as children of the surface visual. 663 mRootVisual->RemoveAllVisuals(); 664 } 665 666 for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) { 667 auto surface_it = mDCSurfaces.find(*it); 668 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); 669 const auto surface = surface_it->second.get(); 670 // Ensure surface is trimmed to updated tile valid rects 671 surface->UpdateAllocatedRect(); 672 if (!same) { 673 const auto visual = surface->GetRootVisual(); 674 if (UseLayerCompositor()) { 675 // Layer compositor expects front to back. 676 mRootVisual->AddVisual(visual, true, nullptr); 677 } else { 678 // Native compositor expects back to front. 679 mRootVisual->AddVisual(visual, false, nullptr); 680 } 681 } 682 } 683 684 mPrevLayers.swap(mCurrentLayers); 685 mCurrentLayers.clear(); 686 687 if (!same || !UseLayerCompositor()) { 688 mPendingCommit = true; 689 } 690 691 MaybeCommit(); 692 693 auto end = TimeStamp::Now(); 694 mozilla::glean::gfx::composite_swap_time.AccumulateSingleSample( 695 (end - start).ToMilliseconds() * 10.); 696 697 // Remove any framebuffers that haven't been 698 // used in the last 60 frames. 699 // 700 // This should use nsTArray::RemoveElementsBy once 701 // CachedFrameBuffer is able to properly destroy 702 // itself in the destructor. 703 const auto gl = GetGLContext(); 704 for (uint32_t i = 0, len = mFrameBuffers.Length(); i < len; ++i) { 705 auto& fb = mFrameBuffers[i]; 706 if ((mCurrentFrame - fb.lastFrameUsed) > 60) { 707 gl->fDeleteRenderbuffers(1, &fb.depthRboId); 708 gl->fDeleteFramebuffers(1, &fb.fboId); 709 mFrameBuffers.UnorderedRemoveElementAt(i); 710 --i; // Examine the element again, if necessary. 711 --len; 712 } 713 } 714 715 if (mEnableAsyncScreenshot && 716 (mCurrentFrame - mAsyncScreenshotLastFrameUsed) > 1) { 717 mEnableAsyncScreenshot = false; 718 } 719 720 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) { 721 return; 722 } 723 724 // Disable video overlay if mCompositionDevice->Commit() with video overlay is 725 // too slow. It drops fps. 726 727 const auto commitDurationMs = 728 static_cast<uint32_t>((end - start).ToMilliseconds()); 729 730 nsPrintfCString marker("CommitWait overlay %u %ums ", 731 (uint8_t)mUsedOverlayTypesInFrame, commitDurationMs); 732 PROFILER_MARKER_TEXT("CommitWait", GRAPHICS, {}, marker); 733 734 for (auto it = mDCSurfaces.begin(); it != mDCSurfaces.end(); it++) { 735 auto* surfaceVideo = it->second->AsDCSurfaceVideo(); 736 if (surfaceVideo) { 737 surfaceVideo->OnCompositorEndFrame(mCurrentFrame, commitDurationMs); 738 } 739 } 740 } 741 742 void DCLayerTree::BindSwapChain(wr::NativeSurfaceId aId, 743 const wr::DeviceIntRect* aDirtyRects, 744 size_t aNumDirtyRects) { 745 auto surface = GetSurface(aId); 746 surface->AsDCLayerSurface()->Bind(aDirtyRects, aNumDirtyRects); 747 } 748 749 void DCLayerTree::PresentSwapChain(wr::NativeSurfaceId aId, 750 const wr::DeviceIntRect* aDirtyRects, 751 size_t aNumDirtyRects) { 752 auto surface = GetSurface(aId); 753 surface->AsDCLayerSurface()->Present(aDirtyRects, aNumDirtyRects); 754 if (surface->AsDCLayerDCompositionTexture()) { 755 mPendingCommit = true; 756 } 757 } 758 759 void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, 760 uint32_t* aFboId, wr::DeviceIntRect aDirtyRect, 761 wr::DeviceIntRect aValidRect) { 762 auto surface = GetSurface(aId.surface_id); 763 auto tile = surface->GetTile(aId.x, aId.y); 764 wr::DeviceIntPoint targetOffset{0, 0}; 765 766 // If tile owns an IDCompositionSurface we use it, otherwise we're using an 767 // IDCompositionVirtualSurface owned by the DCSurface. 768 RefPtr<IDCompositionSurface> compositionSurface; 769 if (surface->mIsVirtualSurface) { 770 gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y, 771 aValidRect.width(), aValidRect.height()); 772 if (!tile->mValidRect.IsEqualEdges(validRect)) { 773 tile->mValidRect = validRect; 774 surface->DirtyAllocatedRect(); 775 } 776 wr::DeviceIntSize tileSize = surface->GetTileSize(); 777 compositionSurface = surface->GetCompositionSurface(); 778 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset(); 779 targetOffset.x = virtualOffset.x + tileSize.width * aId.x; 780 targetOffset.y = virtualOffset.y + tileSize.height * aId.y; 781 } else { 782 compositionSurface = tile->Bind(aValidRect); 783 } 784 785 if (tile->mNeedsFullDraw) { 786 // dcomp requires that the first BeginDraw on a non-virtual surface is the 787 // full size of the pixel buffer. 788 auto tileSize = surface->GetTileSize(); 789 aDirtyRect.min.x = 0; 790 aDirtyRect.min.y = 0; 791 aDirtyRect.max.x = tileSize.width; 792 aDirtyRect.max.y = tileSize.height; 793 tile->mNeedsFullDraw = false; 794 } 795 796 *aFboId = CreateEGLSurfaceForCompositionSurface( 797 aDirtyRect, aOffset, compositionSurface, targetOffset); 798 mCurrentSurface = Some(compositionSurface); 799 } 800 801 void DCLayerTree::Unbind() { 802 if (mCurrentSurface.isNothing()) { 803 return; 804 } 805 806 RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref(); 807 surface->EndDraw(); 808 809 DestroyEGLSurface(); 810 mCurrentSurface = Nothing(); 811 } 812 813 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId, 814 wr::DeviceIntPoint aVirtualOffset, 815 wr::DeviceIntSize aTileSize, bool aIsOpaque) { 816 auto it = mDCSurfaces.find(aId); 817 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); 818 if (it != mDCSurfaces.end()) { 819 // DCSurface already exists. 820 return; 821 } 822 823 // Tile size needs to be positive. 824 if (aTileSize.width <= 0 || aTileSize.height <= 0) { 825 gfxCriticalNote << "TileSize is not positive aId: " << wr::AsUint64(aId) 826 << " aTileSize(" << aTileSize.width << "," 827 << aTileSize.height << ")"; 828 } 829 830 bool isVirtualSurface = 831 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup(); 832 auto surface = MakeUnique<DCSurface>(aTileSize, aVirtualOffset, 833 isVirtualSurface, aIsOpaque, this); 834 if (!surface->Initialize()) { 835 gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId); 836 return; 837 } 838 839 mDCSurfaces[aId] = std::move(surface); 840 } 841 842 void DCLayerTree::CreateSwapChainSurface(wr::NativeSurfaceId aId, 843 wr::DeviceIntSize aSize, 844 bool aIsOpaque, 845 bool aNeedsSyncDcompCommit) { 846 MOZ_ASSERT_IF(mEnableAsyncScreenshot, !aNeedsSyncDcompCommit); 847 848 auto it = mDCSurfaces.find(aId); 849 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); 850 851 UniquePtr<DCSurface> surface; 852 if (UseDCLayerDCompositionTexture()) { 853 surface = MakeUnique<DCLayerDCompositionTexture>(aSize, aIsOpaque, this); 854 if (!surface->Initialize()) { 855 gfxCriticalNote << "Failed to initialize DCLayerDCompositionTexture: " 856 << wr::AsUint64(aId); 857 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); 858 } 859 } else if ( 860 !mEnableAsyncScreenshot && 861 (aNeedsSyncDcompCommit || 862 StaticPrefs:: 863 gfx_webrender_layer_compositor_force_composition_surface_AtStartup())) { 864 surface = MakeUnique<DCLayerCompositionSurface>(aSize, aIsOpaque, this); 865 if (!surface->Initialize()) { 866 gfxCriticalNote << "Failed to initialize DCLayerSurface: " 867 << wr::AsUint64(aId); 868 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); 869 } 870 } else { 871 surface = MakeUnique<DCSwapChain>(aSize, aIsOpaque, this); 872 if (!surface->Initialize()) { 873 gfxCriticalNote << "Failed to initialize DCSwapChain: " 874 << wr::AsUint64(aId); 875 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); 876 } 877 } 878 879 MOZ_ASSERT_IF(mEnableAsyncScreenshot, mDCSurfaces.empty()); 880 881 mDCSurfaces[aId] = std::move(surface); 882 } 883 884 void DCLayerTree::ResizeSwapChainSurface(wr::NativeSurfaceId aId, 885 wr::DeviceIntSize aSize) { 886 auto it = mDCSurfaces.find(aId); 887 MOZ_RELEASE_ASSERT(it != mDCSurfaces.end()); 888 auto surface = it->second.get(); 889 890 mPendingCommit = true; 891 892 if (!surface->AsDCLayerSurface()->Resize(aSize)) { 893 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); 894 } 895 } 896 897 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId, 898 bool aIsOpaque) { 899 auto it = mDCSurfaces.find(aId); 900 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); 901 902 auto surface = MakeUnique<DCExternalSurfaceWrapper>(aIsOpaque, this); 903 if (!surface->Initialize()) { 904 gfxCriticalNote << "Failed to initialize DCExternalSurfaceWrapper: " 905 << wr::AsUint64(aId); 906 return; 907 } 908 909 mDCSurfaces[aId] = std::move(surface); 910 } 911 912 void DCLayerTree::DestroySurface(NativeSurfaceId aId) { 913 auto surface_it = mDCSurfaces.find(aId); 914 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); 915 auto surface = surface_it->second.get(); 916 917 mRootVisual->RemoveVisual(surface->GetRootVisual()); 918 mDCSurfaces.erase(surface_it); 919 } 920 921 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) { 922 auto surface = GetSurface(aId); 923 surface->CreateTile(aX, aY); 924 } 925 926 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) { 927 auto surface = GetSurface(aId); 928 surface->DestroyTile(aX, aY); 929 } 930 931 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId, 932 wr::ExternalImageId aExternalImage) { 933 auto surface_it = mDCSurfaces.find(aId); 934 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); 935 surface_it->second->AttachExternalImage(aExternalImage); 936 } 937 938 void DCExternalSurfaceWrapper::AttachExternalImage( 939 wr::ExternalImageId aExternalImage) { 940 if (auto* surface = EnsureSurfaceForExternalImage(aExternalImage)) { 941 surface->AttachExternalImage(aExternalImage); 942 } 943 } 944 945 template <class ToT> 946 struct QI { 947 template <class FromT> 948 [[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) { 949 RefPtr<ToT> to; 950 (void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to))); 951 return to; 952 } 953 }; 954 955 DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage( 956 wr::ExternalImageId aExternalImage) { 957 if (mSurface) { 958 return mSurface.get(); 959 } 960 961 // Create a new surface based on the texture type. 962 RenderTextureHost* texture = 963 RenderThread::Get()->GetRenderTexture(aExternalImage); 964 if (texture && texture->AsRenderDXGITextureHost()) { 965 auto format = texture->GetFormat(); 966 if (format == gfx::SurfaceFormat::B8G8R8A8 || 967 format == gfx::SurfaceFormat::B8G8R8X8) { 968 MOZ_ASSERT(RenderDXGITextureHost::UseDCompositionTextureOverlay(format)); 969 mSurface.reset( 970 new DCSurfaceDCompositionTextureOverlay(mIsOpaque, mDCLayerTree)); 971 if (!mSurface->Initialize()) { 972 gfxCriticalNote 973 << "Failed to initialize DCSurfaceDCompositionTextureOverlay: " 974 << wr::AsUint64(aExternalImage); 975 mSurface = nullptr; 976 } 977 } else { 978 mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree)); 979 if (!mSurface->Initialize()) { 980 gfxCriticalNote << "Failed to initialize DCSurfaceVideo: " 981 << wr::AsUint64(aExternalImage); 982 mSurface = nullptr; 983 } 984 } 985 } else if (texture && texture->AsRenderDcompSurfaceTextureHost()) { 986 mSurface.reset(new DCSurfaceHandle(mIsOpaque, mDCLayerTree)); 987 if (!mSurface->Initialize()) { 988 gfxCriticalNote << "Failed to initialize DCSurfaceHandle: " 989 << wr::AsUint64(aExternalImage); 990 mSurface = nullptr; 991 } 992 } 993 if (!mSurface) { 994 gfxCriticalNote << "Failed to create a surface for external image: " 995 << gfx::hexa(texture); 996 return nullptr; 997 } 998 999 // Add surface's visual which will contain video data to our root visual. 1000 const auto surfaceVisual = mSurface->GetRootVisual(); 1001 mContentVisual->AddVisual(surfaceVisual, true, nullptr); 1002 1003 // - 1004 // Apply color management. 1005 1006 [&]() { 1007 if (!StaticPrefs::gfx_webrender_dcomp_color_manage_with_filters()) return; 1008 1009 const auto cmsMode = GfxColorManagementMode(); 1010 if (cmsMode == CMSMode::Off) return; 1011 1012 const auto dcomp = mDCLayerTree->GetCompositionDevice(); 1013 const auto dcomp3 = QI<IDCompositionDevice3>::From(dcomp); 1014 if (!dcomp3) { 1015 NS_WARNING( 1016 "No IDCompositionDevice3, cannot use dcomp for color management."); 1017 return; 1018 } 1019 1020 // - 1021 1022 const auto cspace = [&]() { 1023 const auto rangedCspace = texture->GetYUVColorSpace(); 1024 const auto info = FromYUVRangedColorSpace(rangedCspace); 1025 auto ret = ToColorSpace2(info.space); 1026 if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) { 1027 ret = gfx::ColorSpace2::SRGB; 1028 } 1029 return ret; 1030 }(); 1031 1032 const bool rec709GammaAsSrgb = 1033 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb(); 1034 const bool rec2020GammaAsRec709 = 1035 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709(); 1036 1037 auto cspaceDesc = color::ColorspaceDesc{}; 1038 switch (cspace) { 1039 case gfx::ColorSpace2::Display: 1040 return; // No color management needed! 1041 case gfx::ColorSpace2::SRGB: 1042 cspaceDesc.chrom = color::Chromaticities::Srgb(); 1043 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); 1044 break; 1045 1046 case gfx::ColorSpace2::DISPLAY_P3: 1047 cspaceDesc.chrom = color::Chromaticities::DisplayP3(); 1048 cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3(); 1049 break; 1050 1051 case gfx::ColorSpace2::BT601_525: 1052 cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc(); 1053 if (rec709GammaAsSrgb) { 1054 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); 1055 } else { 1056 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709(); 1057 } 1058 break; 1059 1060 case gfx::ColorSpace2::BT709: 1061 cspaceDesc.chrom = color::Chromaticities::Rec709(); 1062 if (rec709GammaAsSrgb) { 1063 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); 1064 } else { 1065 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709(); 1066 } 1067 break; 1068 1069 case gfx::ColorSpace2::BT2020: 1070 cspaceDesc.chrom = color::Chromaticities::Rec2020(); 1071 if (rec2020GammaAsRec709 && rec709GammaAsSrgb) { 1072 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb(); 1073 } else if (rec2020GammaAsRec709) { 1074 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709(); 1075 } else { 1076 // Just Rec709 with slightly more precision. 1077 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit(); 1078 } 1079 break; 1080 } 1081 1082 const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc); 1083 auto cprofileOut = mDCLayerTree->OutputColorProfile(); 1084 bool pretendSrgb = true; 1085 if (pretendSrgb) { 1086 cprofileOut = color::ColorProfileDesc::From(color::ColorspaceDesc{ 1087 .chrom = color::Chromaticities::Srgb(), 1088 .tf = color::PiecewiseGammaDesc::Srgb(), 1089 }); 1090 } 1091 const auto conversion = color::ColorProfileConversionDesc::From({ 1092 .src = cprofileIn, 1093 .dst = cprofileOut, 1094 }); 1095 1096 // - 1097 1098 auto chain = ColorManagementChain::From(*dcomp3, conversion); 1099 mCManageChain = Some(chain); 1100 1101 surfaceVisual->SetEffect(mCManageChain->last.get()); 1102 }(); 1103 1104 return mSurface.get(); 1105 } 1106 1107 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix& aTransform) { 1108 MOZ_ASSERT(mSurface); 1109 if (auto* surface = mSurface->AsDCSurfaceVideo()) { 1110 if (surface->CalculateSwapChainSize(aTransform)) { 1111 surface->PresentVideo(); 1112 } 1113 } else if (auto* surface = mSurface->AsDCSurfaceHandle()) { 1114 surface->PresentSurfaceHandle(); 1115 } else if (auto* surface = 1116 mSurface->AsDCSurfaceDCompositionTextureOverlay()) { 1117 surface->Present(); 1118 } 1119 } 1120 1121 template <typename T> 1122 static inline D2D1_RECT_F D2DRect(const T& aRect) { 1123 return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost()); 1124 } 1125 1126 static inline D2D1_MATRIX_3X2_F D2DMatrix(const gfx::Matrix& aTransform) { 1127 return D2D1::Matrix3x2F(aTransform._11, aTransform._12, aTransform._21, 1128 aTransform._22, aTransform._31, aTransform._32); 1129 } 1130 1131 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId, 1132 const wr::CompositorSurfaceTransform& aTransform, 1133 wr::DeviceIntRect aClipRect, 1134 wr::ImageRendering aImageRendering, 1135 wr::DeviceIntRect aRoundedClipRect, 1136 wr::ClipRadius aClipRadius) { 1137 auto it = mDCSurfaces.find(aId); 1138 MOZ_RELEASE_ASSERT(it != mDCSurfaces.end()); 1139 const auto surface = it->second.get(); 1140 const auto visual = surface->GetContentVisual(); 1141 1142 float sx = aTransform.scale.x; 1143 float sy = aTransform.scale.y; 1144 float tx = aTransform.offset.x; 1145 float ty = aTransform.offset.y; 1146 gfx::Matrix transform(sx, 0.0, 0.0, sy, tx, ty); 1147 1148 surface->PresentExternalSurface(transform); 1149 1150 if (UseLayerCompositor() && 1151 !surface->IsUpdated(aTransform, aClipRect, aImageRendering, 1152 aRoundedClipRect, aClipRadius)) { 1153 mCurrentLayers.push_back(aId); 1154 return; 1155 } 1156 1157 mPendingCommit = true; 1158 1159 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset(); 1160 transform.PreTranslate(-virtualOffset.x, -virtualOffset.y); 1161 1162 // The DirectComposition API applies clipping *before* any 1163 // transforms/offset, whereas we want the clip applied after. Right now, we 1164 // only support rectilinear transforms, and then we transform our clip into 1165 // pre-transform coordinate space for it to be applied there. 1166 // DirectComposition does have an option for pre-transform clipping, if you 1167 // create an explicit IDCompositionEffectGroup object and set a 3D transform 1168 // on that. I suspect that will perform worse though, so we should only do 1169 // that for complex transforms (which are never provided right now). 1170 MOZ_ASSERT(transform.IsRectilinear()); 1171 gfx::Rect clip = transform.Inverse().TransformBounds(gfx::Rect( 1172 aClipRect.min.x, aClipRect.min.y, aClipRect.width(), aClipRect.height())); 1173 // Set the clip rect - converting from world space to the pre-offset space 1174 // that DC requires for rectangle clips. 1175 visual->SetClip(D2DRect(clip)); 1176 1177 // TODO: The input matrix is a 4x4, but we only support a 3x2 at 1178 // the D3D API level (unless we QI to IDCompositionVisual3, which might 1179 // not be available?). 1180 // Should we assert here, or restrict at the WR API level. 1181 visual->SetTransform(D2DMatrix(transform)); 1182 1183 if (aImageRendering == wr::ImageRendering::Auto) { 1184 visual->SetBitmapInterpolationMode( 1185 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR); 1186 } else { 1187 visual->SetBitmapInterpolationMode( 1188 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); 1189 } 1190 1191 surface->SetClip(aRoundedClipRect, aClipRadius); 1192 1193 mCurrentLayers.push_back(aId); 1194 } 1195 1196 GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) { 1197 const auto gl = GetGLContext(); 1198 GLuint fboId = 0; 1199 1200 // Check if we have a cached FBO with matching dimensions 1201 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) { 1202 if (it->width == aWidth && it->height == aHeight) { 1203 fboId = it->fboId; 1204 it->lastFrameUsed = mCurrentFrame; 1205 break; 1206 } 1207 } 1208 1209 // If not, create a new FBO with attached depth buffer 1210 if (fboId == 0) { 1211 // Create the depth buffer 1212 GLuint depthRboId; 1213 gl->fGenRenderbuffers(1, &depthRboId); 1214 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRboId); 1215 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_DEPTH_COMPONENT24, 1216 aWidth, aHeight); 1217 1218 // Create the framebuffer and attach the depth buffer to it 1219 gl->fGenFramebuffers(1, &fboId); 1220 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId); 1221 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER, 1222 LOCAL_GL_DEPTH_ATTACHMENT, 1223 LOCAL_GL_RENDERBUFFER, depthRboId); 1224 1225 // Store this in the cache for future calls. 1226 // TODO(gw): Maybe we should periodically scan this list and remove old 1227 // entries that 1228 // haven't been used for some time? 1229 DCLayerTree::CachedFrameBuffer frame_buffer_info; 1230 frame_buffer_info.width = aWidth; 1231 frame_buffer_info.height = aHeight; 1232 frame_buffer_info.fboId = fboId; 1233 frame_buffer_info.depthRboId = depthRboId; 1234 frame_buffer_info.lastFrameUsed = mCurrentFrame; 1235 mFrameBuffers.AppendElement(frame_buffer_info); 1236 } 1237 1238 return fboId; 1239 } 1240 1241 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aInputSize, 1242 const gfx::IntSize& aOutputSize) { 1243 HRESULT hr; 1244 1245 if (!mVideoDevice || !mVideoContext) { 1246 return false; 1247 } 1248 1249 if (mVideoProcessor && (aInputSize <= mVideoInputSize) && 1250 (aOutputSize <= mVideoOutputSize)) { 1251 return true; 1252 } 1253 1254 mVideoProcessor = nullptr; 1255 mVideoProcessorEnumerator = nullptr; 1256 1257 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {}; 1258 desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE; 1259 desc.InputFrameRate.Numerator = 60; 1260 desc.InputFrameRate.Denominator = 1; 1261 desc.InputWidth = aInputSize.width; 1262 desc.InputHeight = aInputSize.height; 1263 desc.OutputFrameRate.Numerator = 60; 1264 desc.OutputFrameRate.Denominator = 1; 1265 desc.OutputWidth = aOutputSize.width; 1266 desc.OutputHeight = aOutputSize.height; 1267 desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL; 1268 1269 hr = mVideoDevice->CreateVideoProcessorEnumerator( 1270 &desc, getter_AddRefs(mVideoProcessorEnumerator)); 1271 if (FAILED(hr)) { 1272 gfxCriticalNote << "Failed to create VideoProcessorEnumerator: " 1273 << gfx::hexa(hr); 1274 return false; 1275 } 1276 1277 hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0, 1278 getter_AddRefs(mVideoProcessor)); 1279 if (FAILED(hr)) { 1280 mVideoProcessor = nullptr; 1281 mVideoProcessorEnumerator = nullptr; 1282 gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr); 1283 return false; 1284 } 1285 1286 // Reduce power cosumption 1287 // By default, the driver might perform certain processing tasks 1288 // automatically 1289 mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0, 1290 FALSE); 1291 1292 mVideoInputSize = aInputSize; 1293 mVideoOutputSize = aOutputSize; 1294 1295 return true; 1296 } 1297 1298 bool DCLayerTree::SupportsHardwareOverlays() { 1299 return sGpuOverlayInfo->mSupportsHardwareOverlays; 1300 } 1301 1302 bool DCLayerTree::SupportsSwapChainTearing() { 1303 RefPtr<ID3D11Device> device = mDevice; 1304 static const bool supported = [device] { 1305 RefPtr<IDXGIDevice> dxgiDevice; 1306 RefPtr<IDXGIAdapter> adapter; 1307 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); 1308 dxgiDevice->GetAdapter(getter_AddRefs(adapter)); 1309 1310 RefPtr<IDXGIFactory5> dxgiFactory; 1311 HRESULT hr = adapter->GetParent( 1312 IID_PPV_ARGS((IDXGIFactory5**)getter_AddRefs(dxgiFactory))); 1313 if (FAILED(hr)) { 1314 return false; 1315 } 1316 1317 BOOL presentAllowTearing = FALSE; 1318 hr = dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, 1319 &presentAllowTearing, 1320 sizeof(presentAllowTearing)); 1321 if (FAILED(hr)) { 1322 return false; 1323 } 1324 1325 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) { 1326 gpuParent->NotifySwapChainInfo( 1327 layers::SwapChainInfo(!!presentAllowTearing)); 1328 } else if (XRE_IsParentProcess()) { 1329 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 1330 } 1331 return !!presentAllowTearing; 1332 }(); 1333 1334 if (!StaticPrefs::gfx_webrender_swap_chain_allow_tearing_AtStartup()) { 1335 return false; 1336 } 1337 1338 return supported; 1339 } 1340 1341 bool DCLayerTree::UseDCLayerDCompositionTexture() { 1342 if (!gfx::gfxVars::WebRenderLayerCompositorDCompTexture()) { 1343 return false; 1344 } 1345 return gfx::DeviceManagerDx::Get()->CanUseDCompositionTexture(); 1346 } 1347 1348 DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() { 1349 return sGpuOverlayInfo->mOverlayFormatUsed; 1350 } 1351 1352 static layers::OverlaySupportType FlagsToOverlaySupportType( 1353 UINT aFlags, bool aSoftwareOverlaySupported) { 1354 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) { 1355 return layers::OverlaySupportType::Scaling; 1356 } 1357 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT) { 1358 return layers::OverlaySupportType::Direct; 1359 } 1360 if (aSoftwareOverlaySupported) { 1361 return layers::OverlaySupportType::Software; 1362 } 1363 return layers::OverlaySupportType::None; 1364 } 1365 1366 layers::OverlayInfo DCLayerTree::GetOverlayInfo() { 1367 layers::OverlayInfo info; 1368 1369 info.mSupportsOverlays = sGpuOverlayInfo->mSupportsHardwareOverlays; 1370 info.mNv12Overlay = 1371 FlagsToOverlaySupportType(sGpuOverlayInfo->mNv12OverlaySupportFlags, 1372 /* aSoftwareOverlaySupported */ false); 1373 info.mYuy2Overlay = 1374 FlagsToOverlaySupportType(sGpuOverlayInfo->mYuy2OverlaySupportFlags, 1375 /* aSoftwareOverlaySupported */ false); 1376 info.mBgra8Overlay = 1377 FlagsToOverlaySupportType(sGpuOverlayInfo->mBgra8OverlaySupportFlags, 1378 /* aSoftwareOverlaySupported */ true); 1379 info.mRgb10a2Overlay = 1380 FlagsToOverlaySupportType(sGpuOverlayInfo->mRgb10a2OverlaySupportFlags, 1381 /* aSoftwareOverlaySupported */ false); 1382 info.mRgba16fOverlay = 1383 FlagsToOverlaySupportType(sGpuOverlayInfo->mRgba16fOverlaySupportFlags, 1384 /* aSoftwareOverlaySupported */ false); 1385 1386 info.mSupportsVpSuperResolution = sGpuOverlayInfo->mSupportsVpSuperResolution; 1387 info.mSupportsVpAutoHDR = sGpuOverlayInfo->mSupportsVpAutoHDR; 1388 info.mSupportsHDR = sGpuOverlayInfo->mSupportsHDR; 1389 1390 return info; 1391 } 1392 1393 void DCLayerTree::SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes) { 1394 mUsedOverlayTypesInFrame |= aTypes; 1395 } 1396 1397 DCSurface::DCSurface(wr::DeviceIntSize aTileSize, 1398 wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface, 1399 bool aIsOpaque, DCLayerTree* aDCLayerTree) 1400 : mIsVirtualSurface(aIsVirtualSurface), 1401 mDCLayerTree(aDCLayerTree), 1402 mTileSize(aTileSize), 1403 mIsOpaque(aIsOpaque), 1404 mAllocatedRectDirty(true), 1405 mVirtualOffset(aVirtualOffset) {} 1406 1407 DCSurface::~DCSurface() {} 1408 1409 bool DCSurface::IsUpdated(const wr::CompositorSurfaceTransform& aTransform, 1410 const wr::DeviceIntRect& aClipRect, 1411 const wr::ImageRendering aImageRendering, 1412 const wr::DeviceIntRect& aRoundedClipRect, 1413 const wr::ClipRadius& aClipRadius) { 1414 if (mDCSurfaceData.isSome() && 1415 mDCSurfaceData.ref().mTransform == aTransform && 1416 mDCSurfaceData.ref().mClipRect == aClipRect && 1417 mDCSurfaceData.ref().mImageRendering == aImageRendering && 1418 mDCSurfaceData.ref().mRoundedClipRect == aRoundedClipRect && 1419 mDCSurfaceData.ref().mClipRadius == aClipRadius) { 1420 return false; 1421 } 1422 mDCSurfaceData = Some(DCSurfaceData(aTransform, aClipRect, aImageRendering, 1423 aRoundedClipRect, aClipRadius)); 1424 return true; 1425 } 1426 1427 bool DCSurface::Initialize() { 1428 // Create a visual for tiles to attach to, whether virtual or not. 1429 HRESULT hr; 1430 const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); 1431 hr = dCompDevice->CreateVisual(getter_AddRefs(mRootVisual)); 1432 if (FAILED(hr)) { 1433 gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr); 1434 return false; 1435 } 1436 hr = dCompDevice->CreateVisual(getter_AddRefs(mContentVisual)); 1437 if (FAILED(hr)) { 1438 gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr); 1439 return false; 1440 } 1441 mRootVisual->AddVisual(mContentVisual, false, nullptr); 1442 hr = dCompDevice->CreateRectangleClip(getter_AddRefs(mClip)); 1443 if (FAILED(hr)) { 1444 gfxCriticalNote << "Failed to create RectangleClip: " << gfx::hexa(hr); 1445 return false; 1446 } 1447 1448 // If virtual surface is enabled, create and attach to visual, in this case 1449 // the tiles won't own visuals or surfaces. 1450 if (mIsVirtualSurface) { 1451 DXGI_ALPHA_MODE alpha_mode = 1452 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; 1453 1454 hr = dCompDevice->CreateVirtualSurface( 1455 VIRTUAL_SURFACE_SIZE, VIRTUAL_SURFACE_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM, 1456 alpha_mode, getter_AddRefs(mVirtualSurface)); 1457 MOZ_ASSERT(SUCCEEDED(hr)); 1458 1459 // Bind the surface memory to this visual 1460 hr = mContentVisual->SetContent(mVirtualSurface); 1461 MOZ_ASSERT(SUCCEEDED(hr)); 1462 } 1463 1464 return true; 1465 } 1466 1467 void DCSurface::SetClip(wr::DeviceIntRect aClipRect, 1468 wr::ClipRadius aClipRadius) { 1469 bool needsClip = 1470 aClipRadius.top_left > 0.0f || aClipRadius.top_right > 0.0f || 1471 aClipRadius.bottom_left > 0.0f || aClipRadius.bottom_right > 0.0f; 1472 1473 if (needsClip) { 1474 mClip->SetLeft(aClipRect.min.x); 1475 mClip->SetRight(aClipRect.max.x); 1476 mClip->SetTop(aClipRect.min.y); 1477 mClip->SetBottom(aClipRect.max.y); 1478 1479 mClip->SetTopLeftRadiusX(aClipRadius.top_left); 1480 mClip->SetTopLeftRadiusY(aClipRadius.top_left); 1481 1482 mClip->SetTopRightRadiusX(aClipRadius.top_right); 1483 mClip->SetTopRightRadiusY(aClipRadius.top_right); 1484 1485 mClip->SetBottomLeftRadiusX(aClipRadius.bottom_left); 1486 mClip->SetBottomLeftRadiusY(aClipRadius.bottom_left); 1487 1488 mClip->SetBottomRightRadiusX(aClipRadius.bottom_right); 1489 mClip->SetBottomRightRadiusY(aClipRadius.bottom_right); 1490 1491 mRootVisual->SetBorderMode(DCOMPOSITION_BORDER_MODE_SOFT); 1492 mRootVisual->SetClip(mClip); 1493 } else { 1494 mRootVisual->SetBorderMode(DCOMPOSITION_BORDER_MODE_INHERIT); 1495 mRootVisual->SetClip(nullptr); 1496 } 1497 } 1498 1499 void DCSurface::CreateTile(int32_t aX, int32_t aY) { 1500 TileKey key(aX, aY); 1501 MOZ_RELEASE_ASSERT(mDCTiles.find(key) == mDCTiles.end()); 1502 1503 auto tile = MakeUnique<DCTile>(mDCLayerTree); 1504 if (!tile->Initialize(aX, aY, mTileSize, mIsVirtualSurface, mIsOpaque, 1505 mContentVisual)) { 1506 gfxCriticalNote << "Failed to initialize DCTile: " << aX << aY; 1507 return; 1508 } 1509 1510 if (mIsVirtualSurface) { 1511 mAllocatedRectDirty = true; 1512 } else { 1513 mContentVisual->AddVisual(tile->GetVisual(), false, nullptr); 1514 } 1515 1516 mDCTiles[key] = std::move(tile); 1517 } 1518 1519 void DCSurface::DestroyTile(int32_t aX, int32_t aY) { 1520 TileKey key(aX, aY); 1521 if (mIsVirtualSurface) { 1522 mAllocatedRectDirty = true; 1523 } else { 1524 auto tile = GetTile(aX, aY); 1525 mContentVisual->RemoveVisual(tile->GetVisual()); 1526 } 1527 mDCTiles.erase(key); 1528 } 1529 1530 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty = true; } 1531 1532 void DCSurface::UpdateAllocatedRect() { 1533 if (mAllocatedRectDirty) { 1534 if (mVirtualSurface) { 1535 // The virtual surface may have holes in it (for example, an empty tile 1536 // that has no primitives). Instead of trimming to a single bounding 1537 // rect, supply the rect of each valid tile to handle this case. 1538 std::vector<RECT> validRects; 1539 1540 for (auto it = mDCTiles.begin(); it != mDCTiles.end(); ++it) { 1541 auto tile = GetTile(it->first.mX, it->first.mY); 1542 RECT rect; 1543 1544 rect.left = (LONG)(mVirtualOffset.x + it->first.mX * mTileSize.width + 1545 tile->mValidRect.x); 1546 rect.top = (LONG)(mVirtualOffset.y + it->first.mY * mTileSize.height + 1547 tile->mValidRect.y); 1548 rect.right = rect.left + tile->mValidRect.width; 1549 rect.bottom = rect.top + tile->mValidRect.height; 1550 1551 validRects.push_back(rect); 1552 } 1553 1554 mVirtualSurface->Trim(validRects.data(), validRects.size()); 1555 } 1556 // When not using a virtual surface, we still want to reset this 1557 mAllocatedRectDirty = false; 1558 } 1559 } 1560 1561 DCTile* DCSurface::GetTile(int32_t aX, int32_t aY) const { 1562 TileKey key(aX, aY); 1563 auto tile_it = mDCTiles.find(key); 1564 MOZ_RELEASE_ASSERT(tile_it != mDCTiles.end()); 1565 return tile_it->second.get(); 1566 } 1567 1568 DCLayerDCompositionTexture::TextureHolder::TextureHolder( 1569 ID3D11Texture2D* aTexture, IDCompositionTexture* aDCompositionTexture, 1570 EGLSurface aEGLSurface) 1571 : mTexture(aTexture), 1572 mDCompositionTexture(aDCompositionTexture), 1573 mEGLSurface(aEGLSurface) {} 1574 1575 DCLayerDCompositionTexture::DCLayerDCompositionTexture( 1576 wr::DeviceIntSize aSize, bool aIsOpaque, DCLayerTree* aDCLayerTree) 1577 : DCLayerSurface(aIsOpaque, aDCLayerTree), 1578 mSwapChainBufferCount(gfx::gfxVars::UseWebRenderTripleBufferingWin() ? 3 1579 : 2), 1580 mSize(aSize) {} 1581 1582 DCLayerDCompositionTexture::~DCLayerDCompositionTexture() { DestroyTextures(); } 1583 1584 bool DCLayerDCompositionTexture::Initialize() { 1585 DCSurface::Initialize(); 1586 1587 if (!AllocateTextures()) { 1588 return false; 1589 } 1590 return true; 1591 } 1592 1593 bool DCLayerDCompositionTexture::AllocateTextures() { 1594 MOZ_ASSERT(mAvailableTextureHolders.empty()); 1595 1596 HRESULT hr; 1597 const auto device = mDCLayerTree->GetDevice(); 1598 const auto dcomp = mDCLayerTree->GetCompositionDevice(); 1599 const auto dcomp4 = QI<IDCompositionDevice4>::From(dcomp); 1600 if (!dcomp4) { 1601 return false; 1602 } 1603 1604 const auto gl = mDCLayerTree->GetGLContext(); 1605 const auto& gle = gl::GLContextEGL::Cast(gl); 1606 const auto& egl = gle->mEgl; 1607 const EGLConfig eglConfig = mDCLayerTree->GetEGLConfig(); 1608 1609 CD3D11_TEXTURE2D_DESC desc( 1610 DXGI_FORMAT_B8G8R8A8_UNORM, mSize.width, mSize.height, 1, 1, 1611 D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET); 1612 1613 desc.MiscFlags = 1614 D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED; 1615 1616 for (size_t i = 0; i < mSwapChainBufferCount; i++) { 1617 // Allocate ID3D11Texture2D 1618 RefPtr<ID3D11Texture2D> texture; 1619 hr = device->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); 1620 if (FAILED(hr)) { 1621 gfxCriticalNoteOnce << "CreateTexture2D failed: " << gfx::hexa(hr); 1622 return false; 1623 } 1624 1625 // Allocate IDCompositionTexture 1626 RefPtr<IDCompositionTexture> dcompTexture; 1627 hr = 1628 dcomp4->CreateCompositionTexture(texture, getter_AddRefs(dcompTexture)); 1629 if (FAILED(hr)) { 1630 gfxCriticalNoteOnce << "CreateCompositionTexture failed: " 1631 << gfx::hexa(hr); 1632 return false; 1633 } 1634 1635 const auto alphaMode = 1636 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; 1637 dcompTexture->SetAlphaMode(alphaMode); 1638 // XXX 1639 // dcompTexture->SetColorSpace(); 1640 1641 // Allocate mEGLSurface 1642 EGLSurface surface = EGL_NO_SURFACE; 1643 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, mSize.width, 1644 LOCAL_EGL_HEIGHT, mSize.height, 1645 LOCAL_EGL_NONE}; 1646 const auto buffer = reinterpret_cast<EGLClientBuffer>(texture.get()); 1647 1648 surface = egl->fCreatePbufferFromClientBuffer( 1649 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, eglConfig, pbuffer_attribs); 1650 if (!surface) { 1651 EGLint err = egl->mLib->fGetError(); 1652 gfxCriticalNote << "Failed to create Pbuffer error: " << gfx::hexa(err) 1653 << " Size : " 1654 << LayoutDeviceIntSize(mSize.width, mSize.height); 1655 return false; 1656 } 1657 1658 auto textureHolder = 1659 MakeUnique<TextureHolder>(texture, dcompTexture, surface); 1660 mAvailableTextureHolders.push_back(std::move(textureHolder)); 1661 } 1662 1663 MOZ_ASSERT(mAvailableTextureHolders.size() == mSwapChainBufferCount); 1664 1665 return true; 1666 } 1667 1668 void DCLayerDCompositionTexture::DestroyTextures() { 1669 const auto gl = mDCLayerTree->GetGLContext(); 1670 const auto& gle = gl::GLContextEGL::Cast(gl); 1671 const auto& egl = gle->mEgl; 1672 1673 if (mCurrentTextureHolder) { 1674 mAvailableTextureHolders.push_back(std::move(mCurrentTextureHolder)); 1675 } 1676 1677 if (mPresentingTextureHolder) { 1678 mAvailableTextureHolders.push_back(std::move(mPresentingTextureHolder)); 1679 } 1680 1681 while (!mAvailableTextureHolders.empty()) { 1682 auto& front = mAvailableTextureHolders.front(); 1683 1684 if (front->mEGLSurface) { 1685 if (gle->GetEGLSurfaceOverride() == front->mEGLSurface) { 1686 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE); 1687 } 1688 egl->fDestroySurface(front->mEGLSurface); 1689 front->mEGLSurface = EGL_NO_SURFACE; 1690 } 1691 1692 mAvailableTextureHolders.pop_front(); 1693 } 1694 1695 MOZ_ASSERT(!mCurrentTextureHolder); 1696 MOZ_ASSERT(!mPresentingTextureHolder); 1697 MOZ_ASSERT(mAvailableTextureHolders.empty()); 1698 } 1699 1700 UniquePtr<DCLayerDCompositionTexture::TextureHolder> 1701 DCLayerDCompositionTexture::GetNextTexture() { 1702 MOZ_ASSERT(!mAvailableTextureHolders.empty()); 1703 1704 if (mAvailableTextureHolders.empty()) { 1705 return nullptr; 1706 } 1707 1708 UniquePtr<TextureHolder> textureHolder = 1709 std::move(mAvailableTextureHolders.front()); 1710 mAvailableTextureHolders.pop_front(); 1711 1712 return textureHolder; 1713 } 1714 1715 void DCLayerDCompositionTexture::UpdateCurrentTexture() { 1716 if (mCurrentTextureHolder) { 1717 mAvailableTextureHolders.push_back(std::move(mCurrentTextureHolder)); 1718 } 1719 1720 MOZ_ASSERT(!mCurrentTextureHolder); 1721 1722 mCurrentTextureHolder = GetNextTexture(); 1723 } 1724 1725 void DCLayerDCompositionTexture::Bind(const wr::DeviceIntRect* aDirtyRects, 1726 size_t aNumDirtyRects) { 1727 UpdateCurrentTexture(); 1728 1729 if (!mCurrentTextureHolder || 1730 (mCurrentTextureHolder->mEGLSurface == EGL_NO_SURFACE)) { 1731 return; 1732 } 1733 1734 const auto gl = mDCLayerTree->GetGLContext(); 1735 const auto& gle = gl::GLContextEGL::Cast(gl); 1736 1737 gle->SetEGLSurfaceOverride(mCurrentTextureHolder->mEGLSurface); 1738 } 1739 1740 bool DCLayerDCompositionTexture::Resize(wr::DeviceIntSize aSize) { 1741 DestroyTextures(); 1742 mSize = aSize; 1743 bool ret = AllocateTextures(); 1744 return ret; 1745 } 1746 1747 void DCLayerDCompositionTexture::Present(const wr::DeviceIntRect* aDirtyRects, 1748 size_t aNumDirtyRects) { 1749 if (!mCurrentTextureHolder) { 1750 return; 1751 } 1752 1753 if (mPresentingTextureHolder) { 1754 mAvailableTextureHolders.push_back(std::move(mPresentingTextureHolder)); 1755 } 1756 MOZ_ASSERT(!mPresentingTextureHolder); 1757 1758 mPresentingTextureHolder = std::move(mCurrentTextureHolder); 1759 MOZ_ASSERT(!mCurrentTextureHolder); 1760 MOZ_ASSERT(mPresentingTextureHolder); 1761 1762 mContentVisual->SetContent(mPresentingTextureHolder->mDCompositionTexture); 1763 } 1764 1765 DCSwapChain::DCSwapChain(wr::DeviceIntSize aSize, bool aIsOpaque, 1766 DCLayerTree* aDCLayerTree) 1767 : DCLayerSurface(aIsOpaque, aDCLayerTree), 1768 mSwapChainBufferCount(gfx::gfxVars::UseWebRenderTripleBufferingWin() ? 3 1769 : 2), 1770 mSize(aSize), 1771 mEGLSurface(EGL_NO_SURFACE) { 1772 MOZ_ASSERT(mSwapChainBufferCount == 2 || mSwapChainBufferCount == 3); 1773 } 1774 1775 DCSwapChain::~DCSwapChain() { 1776 if (mEGLSurface) { 1777 const auto gl = mDCLayerTree->GetGLContext(); 1778 1779 const auto& gle = gl::GLContextEGL::Cast(gl); 1780 const auto& egl = gle->mEgl; 1781 1782 if (gle->GetEGLSurfaceOverride() == mEGLSurface) { 1783 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE); 1784 } 1785 egl->fDestroySurface(mEGLSurface); 1786 mEGLSurface = EGL_NO_SURFACE; 1787 } 1788 } 1789 1790 bool DCSwapChain::Initialize() { 1791 DCSurface::Initialize(); 1792 1793 const auto gl = mDCLayerTree->GetGLContext(); 1794 const auto& gle = gl::GLContextEGL::Cast(gl); 1795 const auto& egl = gle->mEgl; 1796 1797 HRESULT hr; 1798 auto device = mDCLayerTree->GetDevice(); 1799 1800 RefPtr<IDXGIDevice> dxgiDevice; 1801 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); 1802 1803 RefPtr<IDXGIFactory2> dxgiFactory; 1804 { 1805 RefPtr<IDXGIAdapter> adapter; 1806 dxgiDevice->GetAdapter(getter_AddRefs(adapter)); 1807 adapter->GetParent( 1808 IID_PPV_ARGS((IDXGIFactory2**)getter_AddRefs(dxgiFactory))); 1809 } 1810 1811 DXGI_SWAP_CHAIN_DESC1 desc{}; 1812 desc.Width = mSize.width; 1813 desc.Height = mSize.height; 1814 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 1815 desc.SampleDesc.Count = 1; 1816 desc.SampleDesc.Quality = 0; 1817 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 1818 desc.BufferCount = mSwapChainBufferCount; 1819 // DXGI_SCALING_NONE caused swap chain creation failure. 1820 desc.Scaling = DXGI_SCALING_STRETCH; 1821 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 1822 desc.AlphaMode = 1823 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; 1824 desc.Flags = 0; 1825 if (mDCLayerTree->SupportsSwapChainTearing()) { 1826 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; 1827 } 1828 1829 hr = dxgiFactory->CreateSwapChainForComposition(device, &desc, nullptr, 1830 getter_AddRefs(mSwapChain)); 1831 if (FAILED(hr)) { 1832 gfxCriticalNote << "CreateSwapChainForComposition() failed: " 1833 << gfx::hexa(hr) << " Size : " 1834 << LayoutDeviceIntSize(mSize.width, mSize.height); 1835 return false; 1836 } 1837 mContentVisual->SetContent(mSwapChain); 1838 1839 RefPtr<ID3D11Texture2D> backBuffer; 1840 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), 1841 (void**)getter_AddRefs(backBuffer)); 1842 if (hr == DXGI_ERROR_INVALID_CALL) { 1843 // This happens on some GPUs/drivers when there's a TDR. 1844 if (device->GetDeviceRemovedReason() != S_OK) { 1845 gfxCriticalNote << "GetBuffer returned invalid call: " << gfx::hexa(hr) 1846 << " Size : " 1847 << LayoutDeviceIntSize(mSize.width, mSize.height); 1848 return false; 1849 } 1850 } 1851 1852 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, mSize.width, LOCAL_EGL_HEIGHT, 1853 mSize.height, LOCAL_EGL_NONE}; 1854 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuffer.get()); 1855 EGLConfig eglConfig = mDCLayerTree->GetEGLConfig(); 1856 1857 mEGLSurface = egl->fCreatePbufferFromClientBuffer( 1858 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, eglConfig, pbuffer_attribs); 1859 if (!mEGLSurface) { 1860 EGLint err = egl->mLib->fGetError(); 1861 gfxCriticalNote << "Failed to create Pbuffer error: " << gfx::hexa(err) 1862 << " Size : " 1863 << LayoutDeviceIntSize(mSize.width, mSize.height); 1864 return false; 1865 } 1866 1867 return true; 1868 } 1869 1870 void DCSwapChain::Bind(const wr::DeviceIntRect* aDirtyRects, 1871 size_t aNumDirtyRects) { 1872 const auto gl = mDCLayerTree->GetGLContext(); 1873 const auto& gle = gl::GLContextEGL::Cast(gl); 1874 1875 gle->SetEGLSurfaceOverride(mEGLSurface); 1876 } 1877 1878 bool DCSwapChain::Resize(wr::DeviceIntSize aSize) { 1879 MOZ_ASSERT(mSwapChain); 1880 1881 if (!mSwapChain) { 1882 return false; 1883 } 1884 1885 const auto gl = mDCLayerTree->GetGLContext(); 1886 1887 const auto& gle = gl::GLContextEGL::Cast(gl); 1888 const auto& egl = gle->mEgl; 1889 1890 if (mEGLSurface) { 1891 egl->fDestroySurface(mEGLSurface); 1892 mEGLSurface = EGL_NO_SURFACE; 1893 } 1894 1895 DXGI_SWAP_CHAIN_DESC desc; 1896 HRESULT hr; 1897 1898 mSwapChain->GetDesc(&desc); 1899 1900 UINT flags = mDCLayerTree->SupportsSwapChainTearing() 1901 ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING 1902 : 0; 1903 hr = mSwapChain->ResizeBuffers(desc.BufferCount, aSize.width, aSize.height, 1904 DXGI_FORMAT_B8G8R8A8_UNORM, flags); 1905 if (FAILED(hr)) { 1906 gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr) 1907 << " Size : " 1908 << LayoutDeviceIntSize(aSize.width, aSize.height); 1909 return false; 1910 } 1911 1912 RefPtr<ID3D11Texture2D> backBuffer; 1913 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), 1914 (void**)getter_AddRefs(backBuffer)); 1915 if (hr == DXGI_ERROR_INVALID_CALL) { 1916 auto device = mDCLayerTree->GetDevice(); 1917 // This happens on some GPUs/drivers when there's a TDR. 1918 if (device->GetDeviceRemovedReason() != S_OK) { 1919 gfxCriticalNote << "GetBuffer returned invalid call: " << gfx::hexa(hr) 1920 << " Size : " 1921 << LayoutDeviceIntSize(aSize.width, aSize.height); 1922 return false; 1923 } 1924 } 1925 1926 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, aSize.width, LOCAL_EGL_HEIGHT, 1927 aSize.height, LOCAL_EGL_NONE}; 1928 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuffer.get()); 1929 EGLConfig eglConfig = mDCLayerTree->GetEGLConfig(); 1930 1931 mEGLSurface = egl->fCreatePbufferFromClientBuffer( 1932 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, eglConfig, pbuffer_attribs); 1933 if (!mEGLSurface) { 1934 EGLint err = egl->mLib->fGetError(); 1935 gfxCriticalNote << "Failed to create Pbuffer error: " << gfx::hexa(err) 1936 << " Size : " 1937 << LayoutDeviceIntSize(aSize.width, aSize.height); 1938 return false; 1939 } 1940 1941 mSize = aSize; 1942 return true; 1943 } 1944 1945 void DCSwapChain::Present(const wr::DeviceIntRect* aDirtyRects, 1946 size_t aNumDirtyRects) { 1947 MOZ_ASSERT_IF(aNumDirtyRects > 0, !mFirstPresent); 1948 1949 MOZ_ASSERT(mSwapChain); 1950 1951 if (!mSwapChain) { 1952 return; 1953 } 1954 1955 HRESULT hr = S_OK; 1956 int rectsCount = 0; 1957 StackArray<RECT, 1> rects(aNumDirtyRects); 1958 const UINT flags = 1959 mDCLayerTree->SupportsSwapChainTearing() ? DXGI_PRESENT_ALLOW_TEARING : 0; 1960 1961 if (aNumDirtyRects > 0) { 1962 for (size_t i = 0; i < aNumDirtyRects; ++i) { 1963 const auto& rect = aDirtyRects[i]; 1964 // Clip rect to bufferSize 1965 int left = std::clamp((int)rect.min.x, 0, mSize.width); 1966 int top = std::clamp((int)rect.min.y, 0, mSize.height); 1967 int right = std::clamp((int)rect.max.x, 0, mSize.width); 1968 int bottom = std::clamp((int)rect.max.y, 0, mSize.height); 1969 1970 // When rect is not empty, the rect could be passed to Present1(). 1971 if (left < right && top < bottom) { 1972 rects[rectsCount].left = left; 1973 rects[rectsCount].top = top; 1974 rects[rectsCount].right = right; 1975 rects[rectsCount].bottom = bottom; 1976 rectsCount++; 1977 } 1978 } 1979 1980 if (rectsCount > 0) { 1981 DXGI_PRESENT_PARAMETERS params; 1982 PodZero(¶ms); 1983 params.DirtyRectsCount = rectsCount; 1984 params.pDirtyRects = rects.data(); 1985 1986 hr = mSwapChain->Present1(0, flags, ¶ms); 1987 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { 1988 gfxCriticalNote << "Present1 failed: " << gfx::hexa(hr); 1989 } 1990 } 1991 } else { 1992 mSwapChain->Present(0, flags); 1993 } 1994 1995 if (mFirstPresent) { 1996 mFirstPresent = false; 1997 1998 // Wait for the GPU to finish executing its commands before 1999 // committing the DirectComposition tree, or else the swapchain 2000 // may flicker black when it's first presented. 2001 auto* device = mDCLayerTree->GetDevice(); 2002 RefPtr<IDXGIDevice2> dxgiDevice2; 2003 device->QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice2)); 2004 MOZ_ASSERT(dxgiDevice2); 2005 2006 HANDLE event = ::CreateEvent(nullptr, false, false, nullptr); 2007 hr = dxgiDevice2->EnqueueSetEvent(event); 2008 if (SUCCEEDED(hr)) { 2009 DebugOnly<DWORD> result = ::WaitForSingleObject(event, INFINITE); 2010 MOZ_ASSERT(result == WAIT_OBJECT_0); 2011 } else { 2012 gfxCriticalNoteOnce << "EnqueueSetEvent failed: " << gfx::hexa(hr); 2013 } 2014 ::CloseHandle(event); 2015 } 2016 } 2017 2018 DCLayerCompositionSurface::DCLayerCompositionSurface(wr::DeviceIntSize aSize, 2019 bool aIsOpaque, 2020 DCLayerTree* aDCLayerTree) 2021 : DCLayerSurface(aIsOpaque, aDCLayerTree), mSize(aSize) {} 2022 2023 DCLayerCompositionSurface::~DCLayerCompositionSurface() { 2024 if (mEGLSurface) { 2025 const auto gl = mDCLayerTree->GetGLContext(); 2026 const auto& gle = gl::GLContextEGL::Cast(gl); 2027 const auto& egl = gle->mEgl; 2028 2029 egl->fDestroySurface(mEGLSurface); 2030 mEGLSurface = EGL_NO_SURFACE; 2031 } 2032 } 2033 2034 bool DCLayerCompositionSurface::Initialize() { 2035 DCSurface::Initialize(); 2036 2037 if (!Resize(mSize)) { 2038 return false; 2039 } 2040 return true; 2041 } 2042 2043 void DCLayerCompositionSurface::Bind(const wr::DeviceIntRect* aDirtyRects, 2044 size_t aNumDirtyRects) { 2045 MOZ_ASSERT(mCompositionSurface); 2046 2047 if (!mCompositionSurface) { 2048 return; 2049 } 2050 2051 RefPtr<ID3D11Texture2D> backBuffer; 2052 POINT offset; 2053 HRESULT hr; 2054 2055 RECT updateRect; 2056 gfx::IntPoint updatePos; 2057 if (aNumDirtyRects > 0) { 2058 MOZ_ASSERT(!mFirstDraw); 2059 MOZ_ASSERT(aNumDirtyRects == 1); 2060 2061 updateRect.left = std::clamp(aDirtyRects[0].min.x, 0, mSize.width); 2062 updateRect.top = std::clamp(aDirtyRects[0].min.y, 0, mSize.height); 2063 updateRect.right = std::clamp(aDirtyRects[0].max.x, 0, mSize.width); 2064 updateRect.bottom = std::clamp(aDirtyRects[0].max.y, 0, mSize.height); 2065 2066 updatePos = {updateRect.left, updateRect.top}; 2067 } else { 2068 updateRect.left = 0; 2069 updateRect.top = 0; 2070 updateRect.right = mSize.width; 2071 updateRect.bottom = mSize.height; 2072 2073 updatePos = {0, 0}; 2074 } 2075 2076 mFirstDraw = false; 2077 2078 hr = mCompositionSurface->BeginDraw(&updateRect, __uuidof(ID3D11Texture2D), 2079 (void**)getter_AddRefs(backBuffer), 2080 &offset); 2081 2082 if (FAILED(hr)) { 2083 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW); 2084 return; 2085 } 2086 2087 const auto gl = mDCLayerTree->GetGLContext(); 2088 const auto& gle = gl::GLContextEGL::Cast(gl); 2089 const auto& egl = gle->mEgl; 2090 2091 gfx::IntPoint originOffset = {(int)offset.x - updatePos.x, 2092 (int)offset.y - updatePos.y}; 2093 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, 2094 mSize.width, 2095 LOCAL_EGL_HEIGHT, 2096 mSize.height, 2097 LOCAL_EGL_TEXTURE_OFFSET_X_ANGLE, 2098 originOffset.x, 2099 LOCAL_EGL_TEXTURE_OFFSET_Y_ANGLE, 2100 originOffset.y, 2101 LOCAL_EGL_NONE}; 2102 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuffer.get()); 2103 EGLConfig eglConfig = mDCLayerTree->GetEGLConfig(); 2104 2105 mEGLSurface = egl->fCreatePbufferFromClientBuffer( 2106 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, eglConfig, pbuffer_attribs); 2107 if (!mEGLSurface) { 2108 EGLint err = egl->mLib->fGetError(); 2109 gfxCriticalNote << "Failed to create Pbuffer error: " << gfx::hexa(err) 2110 << " Size : " 2111 << LayoutDeviceIntSize(mSize.width, mSize.height); 2112 return; 2113 } 2114 2115 gle->SetEGLSurfaceOverride(mEGLSurface); 2116 } 2117 2118 bool DCLayerCompositionSurface::Resize(wr::DeviceIntSize aSize) { 2119 MOZ_ASSERT(mEGLSurface == EGL_NO_SURFACE); 2120 2121 if (mSize.width == 0 || mSize.height == 0) { 2122 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 2123 return false; 2124 } 2125 2126 HRESULT hr; 2127 auto* dcompDevice = mDCLayerTree->GetCompositionDevice(); 2128 const auto alphaMode = 2129 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; 2130 2131 RefPtr<IDCompositionSurface> surface; 2132 hr = dcompDevice->CreateSurface(aSize.width, aSize.height, 2133 DXGI_FORMAT_R8G8B8A8_UNORM, alphaMode, 2134 getter_AddRefs(surface)); 2135 if (FAILED(hr)) { 2136 gfxCriticalNote << "Failed to create DCompositionSurface: " 2137 << gfx::hexa(hr); 2138 return false; 2139 } 2140 2141 hr = mContentVisual->SetContent(surface); 2142 if (FAILED(hr)) { 2143 gfxCriticalNote << "Failed to SetContent: " << gfx::hexa(hr); 2144 return false; 2145 } 2146 2147 mCompositionSurface = surface; 2148 mSize = aSize; 2149 mFirstDraw = true; 2150 return true; 2151 } 2152 2153 void DCLayerCompositionSurface::Present(const wr::DeviceIntRect* aDirtyRects, 2154 size_t aNumDirtyRects) { 2155 MOZ_ASSERT(mEGLSurface); 2156 MOZ_ASSERT(mCompositionSurface); 2157 2158 mDCSurfaceData = Nothing(); 2159 2160 if (!mCompositionSurface) { 2161 return; 2162 } 2163 2164 mCompositionSurface->EndDraw(); 2165 2166 if (!mEGLSurface) { 2167 return; 2168 } 2169 2170 const auto gl = mDCLayerTree->GetGLContext(); 2171 const auto& gle = gl::GLContextEGL::Cast(gl); 2172 const auto& egl = gle->mEgl; 2173 2174 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE); 2175 2176 egl->fDestroySurface(mEGLSurface); 2177 mEGLSurface = EGL_NO_SURFACE; 2178 } 2179 2180 DCSurfaceDCompositionTextureOverlay::DCSurfaceDCompositionTextureOverlay( 2181 bool aIsOpaque, DCLayerTree* aDCLayerTree) 2182 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque, 2183 aDCLayerTree) {} 2184 2185 DCSurfaceDCompositionTextureOverlay::~DCSurfaceDCompositionTextureOverlay() {} 2186 2187 void DCSurfaceDCompositionTextureOverlay::AttachExternalImage( 2188 wr::ExternalImageId aExternalImage) { 2189 auto* texture = RenderThread::Get()->GetRenderTexture(aExternalImage); 2190 if (!texture) { 2191 return; 2192 } 2193 mRenderTextureHost = texture; 2194 } 2195 2196 void DCSurfaceDCompositionTextureOverlay::Present() { 2197 if (!mRenderTextureHost) { 2198 return; 2199 } 2200 2201 // Content is not updated 2202 if (mPrevRenderTextureHost == mRenderTextureHost) { 2203 return; 2204 } 2205 2206 const auto textureHost = mRenderTextureHost->AsRenderDXGITextureHost(); 2207 RefPtr<IDCompositionTexture> dcompTexture = 2208 textureHost->GetDCompositionTexture(); 2209 if (!dcompTexture) { 2210 gfxCriticalNote << "Failed to get DCompTexture"; 2211 RenderThread::Get()->NotifyWebRenderError( 2212 WebRenderError::DCOMP_TEXTURE_OVERLAY); 2213 return; 2214 } 2215 2216 const auto alphaMode = 2217 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; 2218 dcompTexture->SetAlphaMode(alphaMode); 2219 // XXX 2220 // dcompTexture->SetColorSpace(); 2221 2222 mContentVisual->SetContent(dcompTexture); 2223 mPrevRenderTextureHost = mRenderTextureHost; 2224 mDCLayerTree->SetPendingCommet(); 2225 } 2226 2227 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree) 2228 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque, 2229 aDCLayerTree), 2230 mSwapChainBufferCount( 2231 StaticPrefs::gfx_webrender_dcomp_video_force_triple_buffering() ? 3 2232 : 2) { 2233 } 2234 2235 DCSurfaceVideo::~DCSurfaceVideo() { 2236 ReleaseDecodeSwapChainResources(); 2237 MOZ_ASSERT(!mSwapChainSurfaceHandle); 2238 } 2239 2240 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) { 2241 switch (aFormat) { 2242 case DXGI_FORMAT_P010: 2243 case DXGI_FORMAT_P016: 2244 case DXGI_FORMAT_NV12: 2245 case DXGI_FORMAT_YUY2: 2246 return true; 2247 default: 2248 return false; 2249 } 2250 } 2251 2252 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) { 2253 auto [texture, usageInfo] = 2254 RenderThread::Get()->GetRenderTextureAndUsageInfo(aExternalImage); 2255 if (!texture) { 2256 gfxCriticalNoteOnce << "Failed to attach ExternalImage for extId:" 2257 << AsUint64(aExternalImage); 2258 mRenderTextureHost = nullptr; 2259 return; 2260 } 2261 2262 if (usageInfo) { 2263 mRenderTextureHostUsageInfo = usageInfo; 2264 } 2265 2266 if (mPrevTexture == texture) { 2267 return; 2268 } 2269 2270 // If the content format is HDR, we will want to use more than 8bit. 2271 mContentIsHDR = false; 2272 if (texture) { 2273 const auto format = texture->GetFormat(); 2274 nsPrintfCString str("AttachExternalImage: SurfaceFormat %d", (int)format); 2275 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str); 2276 switch (format) { 2277 case gfx::SurfaceFormat::R10G10B10A2_UINT32: 2278 case gfx::SurfaceFormat::R10G10B10X2_UINT32: 2279 case gfx::SurfaceFormat::R16G16B16A16F: 2280 case gfx::SurfaceFormat::P010: 2281 case gfx::SurfaceFormat::P016: 2282 mContentIsHDR = true; 2283 break; 2284 default: 2285 break; 2286 } 2287 } 2288 2289 // XXX if software decoded video frame format is nv12, it could be used as 2290 // video overlay. 2291 if (!texture || !texture->AsRenderDXGITextureHost() || 2292 ((texture->GetFormat() != gfx::SurfaceFormat::NV12) && 2293 (texture->GetFormat() != gfx::SurfaceFormat::P010) && 2294 (texture->GetFormat() != gfx::SurfaceFormat::P016))) { 2295 gfxCriticalNote << "Unsupported RenderTexture for overlay: " 2296 << gfx::hexa(texture); 2297 return; 2298 } 2299 2300 mRenderTextureHost = texture; 2301 } 2302 2303 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix& aTransform) { 2304 if (!mRenderTextureHost) { 2305 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 2306 return false; 2307 } 2308 2309 const auto overlayType = mRenderTextureHost->IsSoftwareDecodedVideo() 2310 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO 2311 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO; 2312 mDCLayerTree->SetUsedOverlayTypeInFrame(overlayType); 2313 2314 mVideoSize = mRenderTextureHost->AsRenderDXGITextureHost()->GetSize(0); 2315 2316 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated, 2317 // DCSurfaceVideo::PresentVideo() needs to be called. 2318 bool needsToPresent = mPrevTexture != mRenderTextureHost; 2319 gfx::IntSize swapChainSize = mVideoSize; 2320 gfx::Matrix transform = aTransform; 2321 const bool isDRM = mRenderTextureHost->IsFromDRMSource(); 2322 2323 // When video is rendered to axis aligned integer rectangle, video scaling 2324 // could be done by VideoProcessor 2325 bool scaleVideoAtVideoProcessor = false; 2326 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() && 2327 aTransform.IsTranslation()) { 2328 gfx::Size scaledSize = gfx::Size(mVideoSize) * aTransform.ScaleFactors(); 2329 gfx::IntSize size(int32_t(std::round(scaledSize.width)), 2330 int32_t(std::round(scaledSize.height))); 2331 if (gfx::FuzzyEqual(scaledSize.width, size.width, 0.1f) && 2332 gfx::FuzzyEqual(scaledSize.height, size.height, 0.1f)) { 2333 scaleVideoAtVideoProcessor = true; 2334 swapChainSize = size; 2335 } 2336 } 2337 2338 if (scaleVideoAtVideoProcessor) { 2339 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0 2340 // subsampled formats like NV12 must have an even width and height. 2341 if (swapChainSize.width % 2 == 1) { 2342 swapChainSize.width += 1; 2343 } 2344 if (swapChainSize.height % 2 == 1) { 2345 swapChainSize.height += 1; 2346 } 2347 transform = gfx::Matrix::Translation(aTransform.GetTranslation()); 2348 } 2349 2350 if (!mDCLayerTree->EnsureVideoProcessor(mVideoSize, swapChainSize)) { 2351 gfxCriticalNote << "EnsureVideoProcessor Failed"; 2352 return false; 2353 } 2354 2355 MOZ_ASSERT(mDCLayerTree->GetVideoContext()); 2356 MOZ_ASSERT(mDCLayerTree->GetVideoProcessor()); 2357 2358 const UINT vendorId = GetVendorId(mDCLayerTree->GetVideoDevice()); 2359 const bool driverSupportsAutoHDR = 2360 GetVpAutoHDRSupported(vendorId, mDCLayerTree->GetVideoContext(), 2361 mDCLayerTree->GetVideoProcessor()); 2362 const bool contentIsHDR = mContentIsHDR; 2363 const bool monitorIsHDR = 2364 gfx::DeviceManagerDx::Get()->WindowHDREnabled(mDCLayerTree->GetHwnd()); 2365 const bool powerIsCharging = RenderThread::Get()->GetPowerIsCharging(); 2366 2367 bool useVpAutoHDR = gfx::gfxVars::WebRenderOverlayVpAutoHDR() && 2368 !contentIsHDR && monitorIsHDR && driverSupportsAutoHDR && 2369 powerIsCharging && !mVpAutoHDRFailed; 2370 2371 bool useHDR = 2372 gfx::gfxVars::WebRenderOverlayHDR() && contentIsHDR && monitorIsHDR; 2373 2374 if (profiler_thread_is_being_profiled_for_markers()) { 2375 nsPrintfCString str( 2376 "useVpAutoHDR %d gfxVars %d contentIsHDR %d monitor %d driver %d " 2377 "charging %d failed %d", 2378 useVpAutoHDR, gfx::gfxVars::WebRenderOverlayVpAutoHDR(), contentIsHDR, 2379 monitorIsHDR, driverSupportsAutoHDR, powerIsCharging, mVpAutoHDRFailed); 2380 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str); 2381 } 2382 2383 if (!mVideoSwapChain || mSwapChainSize != swapChainSize || mIsDRM != isDRM || 2384 mUseVpAutoHDR != useVpAutoHDR) { 2385 needsToPresent = true; 2386 ReleaseDecodeSwapChainResources(); 2387 // Update mSwapChainSize before creating SwapChain 2388 mSwapChainSize = swapChainSize; 2389 mIsDRM = isDRM; 2390 2391 auto swapChainFormat = GetSwapChainFormat(useVpAutoHDR, useHDR); 2392 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat); 2393 if (useYUVSwapChain) { 2394 // Tries to create YUV SwapChain 2395 nsPrintfCString str("Creating video swapchain for YUV as DXGI format %d", 2396 (int)swapChainFormat); 2397 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str); 2398 CreateVideoSwapChain(swapChainFormat); 2399 if (!mVideoSwapChain) { 2400 mFailedYuvSwapChain = true; 2401 ReleaseDecodeSwapChainResources(); 2402 2403 gfxCriticalNote << "Fallback to RGB SwapChain"; 2404 } 2405 } 2406 // Tries to create RGB SwapChain 2407 if (!mVideoSwapChain) { 2408 nsPrintfCString str("Creating video swapchain for RGB as DXGI format %d", 2409 (int)swapChainFormat); 2410 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str); 2411 CreateVideoSwapChain(swapChainFormat); 2412 } 2413 if (!mVideoSwapChain && useVpAutoHDR) { 2414 mVpAutoHDRFailed = true; 2415 gfxCriticalNoteOnce << "Failed to create video SwapChain for VpAutoHDR"; 2416 2417 // Disable VpAutoHDR 2418 useVpAutoHDR = false; 2419 swapChainFormat = GetSwapChainFormat(useVpAutoHDR, useHDR); 2420 nsPrintfCString str( 2421 "Creating video swapchain for RGB as DXGI format %d after fallback " 2422 "from VpAutoHDR", 2423 (int)swapChainFormat); 2424 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str); 2425 CreateVideoSwapChain(swapChainFormat); 2426 } 2427 } 2428 2429 aTransform = transform; 2430 mUseVpAutoHDR = useVpAutoHDR; 2431 mUseHDR = useHDR; 2432 2433 return needsToPresent; 2434 } 2435 2436 void DCSurfaceVideo::PresentVideo() { 2437 if (!mRenderTextureHost) { 2438 return; 2439 } 2440 2441 if (!mVideoSwapChain) { 2442 gfxCriticalNote << "Failed to create VideoSwapChain"; 2443 RenderThread::Get()->NotifyWebRenderError( 2444 wr::WebRenderError::VIDEO_OVERLAY); 2445 return; 2446 } 2447 2448 if (!CallVideoProcessorBlt()) { 2449 bool useYUVSwapChain = IsYUVSwapChainFormat(mSwapChainFormat); 2450 if (useYUVSwapChain) { 2451 mFailedYuvSwapChain = true; 2452 ReleaseDecodeSwapChainResources(); 2453 return; 2454 } 2455 RenderThread::Get()->NotifyWebRenderError( 2456 wr::WebRenderError::VIDEO_OVERLAY); 2457 return; 2458 } 2459 2460 const auto device = mDCLayerTree->GetDevice(); 2461 HRESULT hr; 2462 2463 auto start = TimeStamp::Now(); 2464 if (mFirstPresent) { 2465 mFirstPresent = false; 2466 UINT flags = DXGI_PRESENT_USE_DURATION; 2467 // DirectComposition can display black for a swap chain between the first 2468 // and second time it's presented to - maybe the first Present can get lost 2469 // somehow and it shows the wrong buffer. In that case copy the buffers so 2470 // all have the correct contents, which seems to help. The first Present() 2471 // after this needs to have SyncInterval > 0, or else the workaround doesn't 2472 // help. 2473 for (size_t i = 0; i < mSwapChainBufferCount - 1; ++i) { 2474 hr = mVideoSwapChain->Present(0, flags); 2475 // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only 2476 // indicates that the window is occluded and we can stop rendering. 2477 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { 2478 gfxCriticalNoteOnce << "video Present failed during first present: " 2479 << gfx::hexa(hr); 2480 return; 2481 } 2482 2483 RefPtr<ID3D11Texture2D> destTexture; 2484 mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), 2485 (void**)getter_AddRefs(destTexture)); 2486 MOZ_ASSERT(destTexture); 2487 RefPtr<ID3D11Texture2D> srcTexture; 2488 hr = mVideoSwapChain->GetBuffer(1, __uuidof(ID3D11Texture2D), 2489 (void**)getter_AddRefs(srcTexture)); 2490 MOZ_ASSERT(srcTexture); 2491 RefPtr<ID3D11DeviceContext> context; 2492 device->GetImmediateContext(getter_AddRefs(context)); 2493 MOZ_ASSERT(context); 2494 context->CopyResource(destTexture, srcTexture); 2495 } 2496 2497 // Additionally wait for the GPU to finish executing its commands, or 2498 // there still may be a black flicker when presenting expensive content 2499 // (e.g. 4k video). 2500 2501 RefPtr<IDXGIDevice2> dxgiDevice2; 2502 device->QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice2)); 2503 MOZ_ASSERT(dxgiDevice2); 2504 2505 HANDLE event = ::CreateEvent(nullptr, false, false, nullptr); 2506 hr = dxgiDevice2->EnqueueSetEvent(event); 2507 if (SUCCEEDED(hr)) { 2508 DebugOnly<DWORD> result = ::WaitForSingleObject(event, INFINITE); 2509 MOZ_ASSERT(result == WAIT_OBJECT_0); 2510 } else { 2511 gfxCriticalNoteOnce << "EnqueueSetEvent failed: " << gfx::hexa(hr); 2512 } 2513 ::CloseHandle(event); 2514 } 2515 2516 UINT flags = DXGI_PRESENT_USE_DURATION; 2517 UINT interval = 1; 2518 if (StaticPrefs::gfx_webrender_dcomp_video_swap_chain_present_interval_0()) { 2519 interval = 0; 2520 } 2521 2522 hr = mVideoSwapChain->Present(interval, flags); 2523 auto end = TimeStamp::Now(); 2524 2525 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { 2526 gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr); 2527 } 2528 2529 mPrevTexture = mRenderTextureHost; 2530 2531 // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops 2532 // fps. 2533 2534 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) { 2535 return; 2536 } 2537 2538 const auto presentDurationMs = 2539 static_cast<uint32_t>((end - start).ToMilliseconds()); 2540 const auto overlayType = mRenderTextureHost->IsSoftwareDecodedVideo() 2541 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO 2542 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO; 2543 2544 nsPrintfCString marker("PresentWait overlay %u %ums ", (uint8_t)overlayType, 2545 presentDurationMs); 2546 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS, {}, marker); 2547 2548 if (mRenderTextureHostUsageInfo) { 2549 mRenderTextureHostUsageInfo->OnVideoPresent(mDCLayerTree->GetFrameId(), 2550 presentDurationMs); 2551 } 2552 } 2553 2554 void DCSurfaceVideo::OnCompositorEndFrame(int aFrameId, uint32_t aDurationMs) { 2555 if (!mRenderTextureHostUsageInfo) { 2556 return; 2557 } 2558 mRenderTextureHostUsageInfo->OnCompositorEndFrame(aFrameId, aDurationMs); 2559 } 2560 2561 DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat(bool aUseVpAutoHDR, 2562 bool aUseHDR) { 2563 if (aUseVpAutoHDR) { 2564 return DXGI_FORMAT_R16G16B16A16_FLOAT; 2565 } 2566 if (aUseHDR) { 2567 return DXGI_FORMAT_R16G16B16A16_FLOAT; 2568 } 2569 if (mFailedYuvSwapChain || !mDCLayerTree->SupportsHardwareOverlays()) { 2570 return DXGI_FORMAT_B8G8R8A8_UNORM; 2571 } 2572 return mDCLayerTree->GetOverlayFormatForSDR(); 2573 } 2574 2575 bool DCSurfaceVideo::CreateVideoSwapChain(DXGI_FORMAT aSwapChainFormat) { 2576 MOZ_ASSERT(mRenderTextureHost); 2577 2578 mFirstPresent = true; 2579 2580 const auto device = mDCLayerTree->GetDevice(); 2581 2582 RefPtr<IDXGIDevice> dxgiDevice; 2583 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); 2584 2585 RefPtr<IDXGIFactoryMedia> dxgiFactoryMedia; 2586 { 2587 RefPtr<IDXGIAdapter> adapter; 2588 dxgiDevice->GetAdapter(getter_AddRefs(adapter)); 2589 adapter->GetParent( 2590 IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia))); 2591 } 2592 2593 mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle(); 2594 if (!mSwapChainSurfaceHandle) { 2595 gfxCriticalNote << "Failed to create DCompSurfaceHandle"; 2596 return false; 2597 } 2598 2599 DXGI_SWAP_CHAIN_DESC1 desc = {}; 2600 desc.Width = mSwapChainSize.width; 2601 desc.Height = mSwapChainSize.height; 2602 desc.Format = aSwapChainFormat; 2603 desc.Stereo = FALSE; 2604 desc.SampleDesc.Count = 1; 2605 desc.BufferCount = mSwapChainBufferCount; 2606 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 2607 desc.Scaling = DXGI_SCALING_STRETCH; 2608 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 2609 desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO; 2610 if (IsYUVSwapChainFormat(aSwapChainFormat)) { 2611 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO; 2612 } 2613 if (mIsDRM) { 2614 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY; 2615 } 2616 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; 2617 2618 HRESULT hr; 2619 hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle( 2620 device, mSwapChainSurfaceHandle, &desc, nullptr, 2621 getter_AddRefs(mVideoSwapChain)); 2622 2623 if (FAILED(hr)) { 2624 gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr) 2625 << " " << mSwapChainSize; 2626 return false; 2627 } 2628 2629 mSwapChainFormat = aSwapChainFormat; 2630 mContentVisual->SetContent(mVideoSwapChain); 2631 return true; 2632 } 2633 2634 // TODO: Replace with YUVRangedColorSpace 2635 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace( 2636 const gfx::YUVColorSpace aYUVColorSpace, const gfx::ColorRange aColorRange, 2637 const bool aContentIsHDR) { 2638 if (aYUVColorSpace == gfx::YUVColorSpace::BT601) { 2639 if (aColorRange == gfx::ColorRange::FULL) { 2640 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601); 2641 } else { 2642 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601); 2643 } 2644 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) { 2645 if (aColorRange == gfx::ColorRange::FULL) { 2646 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709); 2647 } else { 2648 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709); 2649 } 2650 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) { 2651 // This is a probably-temporary internal workaround for the lack of access 2652 // to mTransferFunction - BT2020 seems to always be used with PQ transfer 2653 // function defined by BT2100 and STMPE 2084, we've/ been making this same 2654 // assumption on macOS for quite some time, so if it was not universally 2655 // true, hopefully bugs would have been filed. 2656 // 2657 // But ideally we'd plumb mTransferFunction through the various structs 2658 // instead, which is a more delicate refactor. 2659 if (StaticPrefs::gfx_color_management_hdr_video_assume_rec2020_uses_pq() && 2660 StaticPrefs::gfx_color_management_hdr_video()) { 2661 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020); 2662 } 2663 if (aColorRange == gfx::ColorRange::FULL) { 2664 if (aContentIsHDR && StaticPrefs::gfx_color_management_hdr_video()) { 2665 // DXGI doesn't have a full range PQ YCbCr format, hopefully we won't 2666 // have to deal with this case. 2667 gfxCriticalNoteOnce 2668 << "GetSourceDXGIColorSpace: DXGI has no full range " 2669 "BT2020 PQ YCbCr format, using studio range instead"; 2670 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020); 2671 } else { 2672 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020); 2673 } 2674 } else { 2675 if (aContentIsHDR && StaticPrefs::gfx_color_management_hdr_video()) { 2676 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020); 2677 } else { 2678 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020); 2679 } 2680 } 2681 } 2682 2683 return Nothing(); 2684 } 2685 2686 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace( 2687 const gfx::YUVRangedColorSpace aYUVColorSpace, const bool aContentIsHDR) { 2688 const auto info = FromYUVRangedColorSpace(aYUVColorSpace); 2689 return GetSourceDXGIColorSpace(info.space, info.range, aContentIsHDR); 2690 } 2691 2692 static Maybe<DXGI_COLOR_SPACE_TYPE> GetOutputDXGIColorSpace( 2693 DXGI_FORMAT aSwapChainFormat, DXGI_COLOR_SPACE_TYPE aInputColorSpace, 2694 bool aUseVpAutoHDR) { 2695 switch (aSwapChainFormat) { 2696 case DXGI_FORMAT_NV12: 2697 case DXGI_FORMAT_YUY2: 2698 return Some(aInputColorSpace); 2699 case DXGI_FORMAT_P010: 2700 case DXGI_FORMAT_P016: 2701 return Some(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020); 2702 case DXGI_FORMAT_R16G16B16A16_FLOAT: 2703 return Some(DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709); 2704 case DXGI_FORMAT_R10G10B10A2_UNORM: 2705 if (aInputColorSpace == DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020) { 2706 // YCbCr BT2100 PQ HDR video being converted to RGB10A2 2707 return Some(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020); 2708 } else if (aInputColorSpace == 2709 DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 || 2710 aInputColorSpace == 2711 DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020) { 2712 return Some(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020); 2713 } 2714 return Some(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709); 2715 case DXGI_FORMAT_R8G8B8A8_UNORM: 2716 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: 2717 case DXGI_FORMAT_B8G8R8A8_UNORM: 2718 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: 2719 case DXGI_FORMAT_B8G8R8X8_UNORM: 2720 case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: 2721 // Refactor note - not sure if mUseVpAutoHDR is ever true here, 2722 // it may only ever use DXGI_FORMAT_R16G16B16A16_FLOAT. 2723 if (aUseVpAutoHDR) { 2724 return Some(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020); 2725 } 2726 return Some(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709); 2727 default: 2728 return Nothing(); 2729 } 2730 } 2731 2732 bool DCSurfaceVideo::CallVideoProcessorBlt() { 2733 MOZ_ASSERT(mRenderTextureHost); 2734 2735 HRESULT hr; 2736 const auto device = mDCLayerTree->GetDevice(); 2737 const auto videoDevice = mDCLayerTree->GetVideoDevice(); 2738 const auto videoContext = mDCLayerTree->GetVideoContext(); 2739 const auto texture = mRenderTextureHost->AsRenderDXGITextureHost(); 2740 2741 Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace = 2742 GetSourceDXGIColorSpace(texture->GetYUVColorSpace(), mContentIsHDR); 2743 if (sourceColorSpace.isNothing()) { 2744 gfxCriticalNote << "Unsupported color space"; 2745 return false; 2746 } 2747 2748 RefPtr<ID3D11Texture2D> texture2D = texture->GetD3D11Texture2DWithGL(); 2749 if (!texture2D) { 2750 gfxCriticalNote << "Failed to get D3D11Texture2D"; 2751 return false; 2752 } 2753 2754 if (!mVideoSwapChain) { 2755 return false; 2756 } 2757 2758 if (texture->mFencesHolderId.isSome()) { 2759 auto* fencesHolderMap = layers::CompositeProcessD3D11FencesHolderMap::Get(); 2760 MOZ_ASSERT(fencesHolderMap); 2761 fencesHolderMap->WaitWriteFence(texture->mFencesHolderId.ref(), device); 2762 } 2763 2764 RefPtr<IDXGISwapChain3> swapChain3; 2765 mVideoSwapChain->QueryInterface( 2766 (IDXGISwapChain3**)getter_AddRefs(swapChain3)); 2767 if (!swapChain3) { 2768 gfxCriticalNote << "Failed to get IDXGISwapChain3"; 2769 return false; 2770 } 2771 2772 RefPtr<ID3D11VideoContext1> videoContext1; 2773 videoContext->QueryInterface( 2774 (ID3D11VideoContext1**)getter_AddRefs(videoContext1)); 2775 if (!videoContext1) { 2776 gfxCriticalNote << "Failed to get ID3D11VideoContext1"; 2777 return false; 2778 } 2779 2780 const auto videoProcessor = mDCLayerTree->GetVideoProcessor(); 2781 const auto videoProcessorEnumerator = 2782 mDCLayerTree->GetVideoProcessorEnumerator(); 2783 2784 DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref(); 2785 videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0, 2786 inputColorSpace); 2787 2788 Maybe<DXGI_COLOR_SPACE_TYPE> outputColorSpace = 2789 GetOutputDXGIColorSpace(mSwapChainFormat, inputColorSpace, mUseVpAutoHDR); 2790 if (outputColorSpace.isNothing()) { 2791 gfxCriticalNoteOnce << "Unrecognized DXGI mSwapChainFormat, unsure of " 2792 "correct DXGI colorspace: " 2793 << gfx::hexa(mSwapChainFormat); 2794 return false; 2795 } 2796 2797 hr = swapChain3->SetColorSpace1(outputColorSpace.ref()); 2798 if (FAILED(hr)) { 2799 gfxCriticalNoteOnce << "SetColorSpace1 failed: " << gfx::hexa(hr); 2800 RenderThread::Get()->NotifyWebRenderError( 2801 wr::WebRenderError::VIDEO_OVERLAY); 2802 return false; 2803 } 2804 videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor, 2805 outputColorSpace.ref()); 2806 2807 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {}; 2808 inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; 2809 inputDesc.Texture2D.ArraySlice = texture->ArrayIndex(); 2810 2811 RefPtr<ID3D11VideoProcessorInputView> inputView; 2812 hr = videoDevice->CreateVideoProcessorInputView( 2813 texture2D, videoProcessorEnumerator, &inputDesc, 2814 getter_AddRefs(inputView)); 2815 if (FAILED(hr)) { 2816 gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: " 2817 << gfx::hexa(hr); 2818 return false; 2819 } 2820 2821 D3D11_VIDEO_PROCESSOR_STREAM stream = {}; 2822 stream.Enable = true; 2823 stream.OutputIndex = 0; 2824 stream.InputFrameOrField = 0; 2825 stream.PastFrames = 0; 2826 stream.FutureFrames = 0; 2827 stream.pInputSurface = inputView.get(); 2828 2829 RECT destRect; 2830 destRect.left = 0; 2831 destRect.top = 0; 2832 destRect.right = mSwapChainSize.width; 2833 destRect.bottom = mSwapChainSize.height; 2834 2835 videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE, 2836 &destRect); 2837 videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE, 2838 &destRect); 2839 RECT sourceRect; 2840 sourceRect.left = 0; 2841 sourceRect.top = 0; 2842 sourceRect.right = mVideoSize.width; 2843 sourceRect.bottom = mVideoSize.height; 2844 videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE, 2845 &sourceRect); 2846 2847 if (!mOutputView) { 2848 RefPtr<ID3D11Texture2D> backBuf; 2849 mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), 2850 (void**)getter_AddRefs(backBuf)); 2851 2852 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {}; 2853 outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; 2854 outputDesc.Texture2D.MipSlice = 0; 2855 2856 hr = videoDevice->CreateVideoProcessorOutputView( 2857 backBuf, videoProcessorEnumerator, &outputDesc, 2858 getter_AddRefs(mOutputView)); 2859 if (FAILED(hr)) { 2860 gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: " 2861 << gfx::hexa(hr); 2862 return false; 2863 } 2864 } 2865 2866 const UINT vendorId = GetVendorId(videoDevice); 2867 const auto powerIsCharging = RenderThread::Get()->GetPowerIsCharging(); 2868 const bool useSuperResolution = 2869 gfx::gfxVars::WebRenderOverlayVpSuperResolution() && powerIsCharging && 2870 !mVpSuperResolutionFailed; 2871 2872 if (profiler_thread_is_being_profiled_for_markers()) { 2873 nsPrintfCString str( 2874 "useSuperResolution %d gfxVars %d charging %d failed %d", 2875 useSuperResolution, gfx::gfxVars::WebRenderOverlayVpSuperResolution(), 2876 powerIsCharging, mVpSuperResolutionFailed); 2877 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, str); 2878 } 2879 2880 if (useSuperResolution) { 2881 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, 2882 "SetVpSuperResolution"_ns); 2883 2884 hr = SetVpSuperResolution(vendorId, videoContext, videoProcessor, true); 2885 if (FAILED(hr)) { 2886 if (hr != E_NOTIMPL) { 2887 gfxCriticalNoteOnce << "SetVpSuperResolution failed: " << gfx::hexa(hr); 2888 } 2889 mVpSuperResolutionFailed = true; 2890 } 2891 } else if (gfx::gfxVars::WebRenderOverlayVpSuperResolution() && 2892 !useSuperResolution) { 2893 SetVpSuperResolution(vendorId, videoContext, videoProcessor, false); 2894 } 2895 2896 if (profiler_thread_is_being_profiled_for_markers() && vendorId == 0x10DE) { 2897 AddProfileMarkerForNvidiaVpSuperResolutionInfo(videoContext, 2898 videoProcessor); 2899 } 2900 2901 if (mUseVpAutoHDR) { 2902 PROFILER_MARKER_TEXT("DCSurfaceVideo", GRAPHICS, {}, "SetVpAutoHDR"_ns); 2903 2904 hr = SetVpAutoHDR(vendorId, videoContext, videoProcessor, true); 2905 if (FAILED(hr)) { 2906 gfxCriticalNoteOnce << "SetVpAutoHDR failed: " << gfx::hexa(hr); 2907 mVpAutoHDRFailed = true; 2908 } 2909 } 2910 2911 hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1, 2912 &stream); 2913 if (FAILED(hr)) { 2914 gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr); 2915 return false; 2916 } 2917 2918 return true; 2919 } 2920 2921 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() { 2922 mOutputView = nullptr; 2923 mVideoSwapChain = nullptr; 2924 mDecodeSwapChain = nullptr; 2925 mDecodeResource = nullptr; 2926 if (mSwapChainSurfaceHandle) { 2927 ::CloseHandle(mSwapChainSurfaceHandle); 2928 mSwapChainSurfaceHandle = 0; 2929 } 2930 mUseVpAutoHDR = false; 2931 mUseHDR = false; 2932 } 2933 2934 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree) 2935 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque, 2936 aDCLayerTree) {} 2937 2938 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage) { 2939 RenderTextureHost* texture = 2940 RenderThread::Get()->GetRenderTexture(aExternalImage); 2941 RenderDcompSurfaceTextureHost* renderTexture = 2942 texture ? texture->AsRenderDcompSurfaceTextureHost() : nullptr; 2943 if (!renderTexture) { 2944 gfxCriticalNote << "Unsupported RenderTexture for DCSurfaceHandle: " 2945 << gfx::hexa(texture); 2946 return; 2947 } 2948 2949 const auto handle = renderTexture->GetDcompSurfaceHandle(); 2950 if (GetSurfaceHandle() == handle) { 2951 return; 2952 } 2953 2954 LOG_H("AttachExternalImage, ext-image=%" PRIu64 ", texture=%p, handle=%p", 2955 wr::AsUint64(aExternalImage), renderTexture, handle); 2956 mDcompTextureHost = renderTexture; 2957 } 2958 2959 HANDLE DCSurfaceHandle::GetSurfaceHandle() const { 2960 if (mDcompTextureHost) { 2961 return mDcompTextureHost->GetDcompSurfaceHandle(); 2962 } 2963 return nullptr; 2964 } 2965 2966 IDCompositionSurface* DCSurfaceHandle::EnsureSurface() { 2967 if (auto* surface = mDcompTextureHost->GetSurface()) { 2968 return surface; 2969 } 2970 2971 // Texture host hasn't created the surface yet, ask it to create a new one. 2972 RefPtr<IDCompositionDevice> device; 2973 HRESULT hr = mDCLayerTree->GetCompositionDevice()->QueryInterface( 2974 (IDCompositionDevice**)getter_AddRefs(device)); 2975 if (FAILED(hr)) { 2976 gfxCriticalNote 2977 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: " 2978 << gfx::hexa(hr); 2979 return nullptr; 2980 } 2981 2982 return mDcompTextureHost->CreateSurfaceFromDevice(device); 2983 } 2984 2985 void DCSurfaceHandle::PresentSurfaceHandle() { 2986 LOG_H("PresentSurfaceHandle"); 2987 if (IDCompositionSurface* surface = EnsureSurface()) { 2988 LOG_H("Set surface %p to visual", surface); 2989 mContentVisual->SetContent(surface); 2990 } else { 2991 mContentVisual->SetContent(nullptr); 2992 } 2993 } 2994 2995 DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {} 2996 2997 DCTile::~DCTile() {} 2998 2999 bool DCTile::Initialize(int aX, int aY, wr::DeviceIntSize aSize, 3000 bool aIsVirtualSurface, bool aIsOpaque, 3001 RefPtr<IDCompositionVisual2> mSurfaceVisual) { 3002 if (aSize.width <= 0 || aSize.height <= 0) { 3003 return false; 3004 } 3005 3006 mSize = aSize; 3007 mIsOpaque = aIsOpaque; 3008 mIsVirtualSurface = aIsVirtualSurface; 3009 mNeedsFullDraw = !aIsVirtualSurface; 3010 3011 if (aIsVirtualSurface) { 3012 // Initially, the entire tile is considered valid, unless it is set by 3013 // the SetTileProperties method. 3014 mValidRect.x = 0; 3015 mValidRect.y = 0; 3016 mValidRect.width = aSize.width; 3017 mValidRect.height = aSize.height; 3018 } else { 3019 HRESULT hr; 3020 const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); 3021 // Create the visual and put it in the tree under the surface visual 3022 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual)); 3023 if (FAILED(hr)) { 3024 gfxCriticalNote << "Failed to CreateVisual for DCTile: " << gfx::hexa(hr); 3025 return false; 3026 } 3027 mSurfaceVisual->AddVisual(mVisual, false, nullptr); 3028 // Position the tile relative to the surface visual 3029 mVisual->SetOffsetX(aX * aSize.width); 3030 mVisual->SetOffsetY(aY * aSize.height); 3031 // Clip the visual so it doesn't show anything until we update it 3032 D2D_RECT_F clip = {0, 0, 0, 0}; 3033 mVisual->SetClip(clip); 3034 // Create the underlying pixel buffer. 3035 mCompositionSurface = CreateCompositionSurface(aSize, aIsOpaque); 3036 if (!mCompositionSurface) { 3037 return false; 3038 } 3039 hr = mVisual->SetContent(mCompositionSurface); 3040 if (FAILED(hr)) { 3041 gfxCriticalNote << "Failed to SetContent for DCTile: " << gfx::hexa(hr); 3042 return false; 3043 } 3044 } 3045 3046 return true; 3047 } 3048 3049 RefPtr<IDCompositionSurface> DCTile::CreateCompositionSurface( 3050 wr::DeviceIntSize aSize, bool aIsOpaque) { 3051 HRESULT hr; 3052 const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); 3053 const auto alphaMode = 3054 aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; 3055 RefPtr<IDCompositionSurface> compositionSurface; 3056 3057 hr = dCompDevice->CreateSurface(aSize.width, aSize.height, 3058 DXGI_FORMAT_R8G8B8A8_UNORM, alphaMode, 3059 getter_AddRefs(compositionSurface)); 3060 if (FAILED(hr)) { 3061 gfxCriticalNote << "Failed to CreateSurface for DCTile: " << gfx::hexa(hr); 3062 return nullptr; 3063 } 3064 return compositionSurface; 3065 } 3066 3067 RefPtr<IDCompositionSurface> DCTile::Bind(wr::DeviceIntRect aValidRect) { 3068 if (mVisual != nullptr) { 3069 // Tile owns a visual, set the size of the visual to match the portion we 3070 // want to be visible. 3071 D2D_RECT_F clip_rect; 3072 clip_rect.left = aValidRect.min.x; 3073 clip_rect.top = aValidRect.min.y; 3074 clip_rect.right = aValidRect.max.x; 3075 clip_rect.bottom = aValidRect.max.y; 3076 mVisual->SetClip(clip_rect); 3077 } 3078 return mCompositionSurface; 3079 } 3080 3081 GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface( 3082 wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset, 3083 RefPtr<IDCompositionSurface> aCompositionSurface, 3084 wr::DeviceIntPoint aSurfaceOffset) { 3085 MOZ_ASSERT(aCompositionSurface.get()); 3086 3087 HRESULT hr; 3088 const auto gl = GetGLContext(); 3089 RefPtr<ID3D11Texture2D> backBuf; 3090 POINT offset; 3091 3092 RECT update_rect; 3093 update_rect.left = aSurfaceOffset.x + aDirtyRect.min.x; 3094 update_rect.top = aSurfaceOffset.y + aDirtyRect.min.y; 3095 update_rect.right = aSurfaceOffset.x + aDirtyRect.max.x; 3096 update_rect.bottom = aSurfaceOffset.y + aDirtyRect.max.y; 3097 hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D), 3098 (void**)getter_AddRefs(backBuf), &offset); 3099 3100 if (FAILED(hr)) { 3101 LayoutDeviceIntRect rect = widget::WinUtils::ToIntRect(update_rect); 3102 3103 gfxCriticalNote << "DCompositionSurface::BeginDraw failed: " 3104 << gfx::hexa(hr) << " " << rect; 3105 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW); 3106 return false; 3107 } 3108 3109 // DC includes the origin of the dirty / update rect in the draw offset, 3110 // undo that here since WR expects it to be an absolute offset. 3111 offset.x -= aDirtyRect.min.x; 3112 offset.y -= aDirtyRect.min.y; 3113 3114 D3D11_TEXTURE2D_DESC desc; 3115 backBuf->GetDesc(&desc); 3116 3117 const auto& gle = gl::GLContextEGL::Cast(gl); 3118 const auto& egl = gle->mEgl; 3119 3120 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get()); 3121 3122 // Construct an EGLImage wrapper around the D3D texture for ANGLE. 3123 const EGLint attribs[] = {LOCAL_EGL_NONE}; 3124 mEGLImage = egl->fCreateImage(EGL_NO_CONTEXT, LOCAL_EGL_D3D11_TEXTURE_ANGLE, 3125 buffer, attribs); 3126 3127 // Get the current FBO and RBO id, so we can restore them later 3128 GLint currentFboId, currentRboId; 3129 gl->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, ¤tFboId); 3130 gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, ¤tRboId); 3131 3132 // Create a render buffer object that is backed by the EGL image. 3133 gl->fGenRenderbuffers(1, &mColorRBO); 3134 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO); 3135 gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage); 3136 3137 // Get or create an FBO for the specified dimensions 3138 GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height); 3139 3140 // Attach the new renderbuffer to the FBO 3141 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId); 3142 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER, 3143 LOCAL_GL_COLOR_ATTACHMENT0, 3144 LOCAL_GL_RENDERBUFFER, mColorRBO); 3145 3146 // Restore previous FBO and RBO bindings 3147 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, currentFboId); 3148 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId); 3149 3150 aOffset->x = offset.x; 3151 aOffset->y = offset.y; 3152 3153 return fboId; 3154 } 3155 3156 void DCLayerTree::DestroyEGLSurface() { 3157 const auto gl = GetGLContext(); 3158 3159 if (mColorRBO) { 3160 gl->fDeleteRenderbuffers(1, &mColorRBO); 3161 mColorRBO = 0; 3162 } 3163 3164 if (mEGLImage) { 3165 const auto& gle = gl::GLContextEGL::Cast(gl); 3166 const auto& egl = gle->mEgl; 3167 egl->fDestroyImage(mEGLImage); 3168 mEGLImage = EGL_NO_IMAGE; 3169 } 3170 } 3171 3172 // - 3173 3174 } // namespace wr 3175 namespace gfx { 3176 3177 color::ColorProfileDesc QueryOutputColorProfile() { 3178 // GPU process can't simply init gfxPlatform, (and we don't need most of it) 3179 // but we do need gfxPlatform::GetCMSOutputProfile(). 3180 // So we steal what we need through the window: 3181 const auto outputProfileData = 3182 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl(); 3183 3184 const auto qcmsProfile = qcms_profile_from_memory( 3185 outputProfileData.Elements(), outputProfileData.Length()); 3186 const auto release = MakeScopeExit([&]() { 3187 if (qcmsProfile) { 3188 qcms_profile_release(qcmsProfile); 3189 } 3190 }); 3191 3192 const bool print = gfxEnv::MOZ_GL_SPEW(); 3193 3194 const auto ret = [&]() { 3195 if (qcmsProfile) { 3196 return color::ColorProfileDesc::From(*qcmsProfile); 3197 } 3198 if (print) { 3199 printf_stderr( 3200 "Missing or failed to load display color profile, defaulting to " 3201 "sRGB.\n"); 3202 } 3203 const auto MISSING_PROFILE_DEFAULT_SPACE = color::ColorspaceDesc{ 3204 color::Chromaticities::Srgb(), 3205 color::PiecewiseGammaDesc::Srgb(), 3206 }; 3207 return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE); 3208 }(); 3209 3210 if (print) { 3211 const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r); 3212 printf_stderr( 3213 "Display profile:\n" 3214 " Approx Gamma: %f\n" 3215 " XYZ-D65 Red : %f, %f, %f\n" 3216 " XYZ-D65 Green: %f, %f, %f\n" 3217 " XYZ-D65 Blue : %f, %f, %f\n", 3218 gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0), 3219 ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2), 3220 3221 ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1), 3222 ret.xyzd65FromLinearRgb.at(1, 2), 3223 3224 ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1), 3225 ret.xyzd65FromLinearRgb.at(2, 2)); 3226 } 3227 3228 return ret; 3229 } 3230 3231 } // namespace gfx 3232 namespace wr { 3233 3234 inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) { 3235 return D2D1_MATRIX_5X4_F{{{ 3236 m.rows[0][0], 3237 m.rows[1][0], 3238 m.rows[2][0], 3239 m.rows[3][0], 3240 m.rows[0][1], 3241 m.rows[1][1], 3242 m.rows[2][1], 3243 m.rows[3][1], 3244 m.rows[0][2], 3245 m.rows[1][2], 3246 m.rows[2][2], 3247 m.rows[3][2], 3248 m.rows[0][3], 3249 m.rows[1][3], 3250 m.rows[2][3], 3251 m.rows[3][3], 3252 0, 3253 0, 3254 0, 3255 0, 3256 }}}; 3257 } 3258 3259 ColorManagementChain ColorManagementChain::From( 3260 IDCompositionDevice3& dcomp, 3261 const color::ColorProfileConversionDesc& conv) { 3262 auto ret = ColorManagementChain{}; 3263 3264 const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) { 3265 if (ret.last) { 3266 afterLast->SetInput(0, ret.last, 0); 3267 } 3268 ret.last = afterLast; 3269 }; 3270 3271 const auto MaybeAppendColorMatrix = [&](const color::mat4& m) { 3272 RefPtr<IDCompositionColorMatrixEffect> e; 3273 if (approx(m, color::mat4::Identity())) return e; 3274 dcomp.CreateColorMatrixEffect(getter_AddRefs(e)); 3275 MOZ_ASSERT(e); 3276 if (!e) return e; 3277 e->SetMatrix(to_D2D1_MATRIX_5X4_F(m)); 3278 Append(e); 3279 return e; 3280 }; 3281 const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) { 3282 RefPtr<IDCompositionTableTransferEffect> e; 3283 if (!t.r.size() && !t.g.size() && !t.b.size()) return e; 3284 dcomp.CreateTableTransferEffect(getter_AddRefs(e)); 3285 MOZ_ASSERT(e); 3286 if (!e) return e; 3287 e->SetRedTable(t.r.data(), t.r.size()); 3288 e->SetGreenTable(t.g.data(), t.g.size()); 3289 e->SetBlueTable(t.b.data(), t.b.size()); 3290 Append(e); 3291 return e; 3292 }; 3293 3294 ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv); 3295 ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf); 3296 ret.dstLinearFromSrcLinear = 3297 MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear)); 3298 ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear); 3299 3300 return ret; 3301 } 3302 3303 ColorManagementChain::~ColorManagementChain() = default; 3304 3305 } // namespace wr 3306 } // namespace mozilla 3307 3308 #undef LOG_H