RenderCompositorANGLE.cpp (34741B)
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 "RenderCompositorANGLE.h" 8 9 #include "GLContext.h" 10 #include "GLContextEGL.h" 11 #include "GLContextProvider.h" 12 #include "mozilla/gfx/DeviceManagerDx.h" 13 #include "mozilla/gfx/gfxVars.h" 14 #include "mozilla/gfx/Logging.h" 15 #include "mozilla/gfx/StackArray.h" 16 #include "mozilla/layers/FenceD3D11.h" 17 #include "mozilla/layers/TextureD3D11.h" 18 #include "mozilla/layers/HelpersD3D11.h" 19 #include "mozilla/layers/SyncObject.h" 20 #include "mozilla/ProfilerMarkers.h" 21 #include "mozilla/StaticPrefs_gfx.h" 22 #include "mozilla/webrender/DCLayerTree.h" 23 #include "mozilla/webrender/RenderThread.h" 24 #include "mozilla/widget/CompositorWidget.h" 25 #include "mozilla/widget/WinCompositorWidget.h" 26 #include "mozilla/glean/GfxMetrics.h" 27 #include "nsPrintfCString.h" 28 #include "FxROutputHandler.h" 29 30 #include <d3d11.h> 31 #include <dcomp.h> 32 #include <dxgi1_2.h> 33 34 // Flag for PrintWindow() that is defined in Winuser.h. It is defined since 35 // Windows 8.1. This allows PrintWindow to capture window content that is 36 // rendered with DirectComposition. 37 #undef PW_RENDERFULLCONTENT 38 #define PW_RENDERFULLCONTENT 0x00000002 39 40 namespace mozilla::wr { 41 42 extern LazyLogModule gRenderThreadLog; 43 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__)) 44 45 /* static */ 46 UniquePtr<RenderCompositor> RenderCompositorANGLE::Create( 47 const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) { 48 RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL(aError); 49 if (!gl) { 50 if (aError.IsEmpty()) { 51 aError.Assign("RcANGLE(no shared GL)"_ns); 52 } else { 53 aError.Append("(Create)"_ns); 54 } 55 return nullptr; 56 } 57 58 UniquePtr<RenderCompositorANGLE> compositor = 59 MakeUnique<RenderCompositorANGLE>(aWidget, std::move(gl)); 60 if (!compositor->Initialize(aError)) { 61 return nullptr; 62 } 63 return compositor; 64 } 65 66 RenderCompositorANGLE::RenderCompositorANGLE( 67 const RefPtr<widget::CompositorWidget>& aWidget, 68 RefPtr<gl::GLContext>&& aGL) 69 : RenderCompositor(aWidget), mGL(aGL) { 70 MOZ_ASSERT(mGL); 71 LOG("RenderCompositorANGLE::RenderCompositorANGLE()"); 72 } 73 74 RenderCompositorANGLE::~RenderCompositorANGLE() { 75 LOG("RenderCompositorANGLE::~RenderCompositorANGLE()"); 76 77 DestroyEGLSurface(); 78 MOZ_ASSERT(!mEGLSurface); 79 } 80 81 ID3D11Device* RenderCompositorANGLE::GetDeviceOfEGLDisplay(nsACString& aError) { 82 const auto& gle = gl::GLContextEGL::Cast(mGL); 83 const auto& egl = gle->mEgl; 84 MOZ_ASSERT(egl); 85 if (!egl || 86 !egl->mLib->IsExtensionSupported(gl::EGLLibExtension::EXT_device_query)) { 87 aError.Assign("RcANGLE(no EXT_device_query support)"_ns); 88 return nullptr; 89 } 90 91 // Fetch the D3D11 device. 92 EGLDeviceEXT eglDevice = nullptr; 93 egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice); 94 MOZ_ASSERT(eglDevice); 95 ID3D11Device* device = nullptr; 96 egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE, 97 (EGLAttrib*)&device); 98 if (!device) { 99 aError.Assign("RcANGLE(get D3D11Device from EGLDisplay failed)"_ns); 100 return nullptr; 101 } 102 return device; 103 } 104 105 bool RenderCompositorANGLE::Initialize(nsACString& aError) { 106 // TODO(aosmond): This causes us to lose WebRender because it is unable to 107 // distinguish why we failed and retry once the reset is complete. This does 108 // appear to happen in the wild, so we really should try to do something 109 // differently here. 110 if (RenderThread::Get()->IsHandlingDeviceReset()) { 111 aError.Assign("RcANGLE(waiting device reset)"_ns); 112 return false; 113 } 114 115 // Force enable alpha channel to make sure ANGLE use correct framebuffer 116 // formart 117 const auto& gle = gl::GLContextEGL::Cast(mGL); 118 const auto& egl = gle->mEgl; 119 if (!gl::CreateConfig(*egl, &mEGLConfig, /* bpp */ 32, 120 /* enableDepthBuffer */ false, mGL->IsGLES())) { 121 aError.Assign("RcANGLE(create EGLConfig failed)"_ns); 122 return false; 123 } 124 MOZ_ASSERT(mEGLConfig); 125 126 mDevice = GetDeviceOfEGLDisplay(aError); 127 if (!mDevice) { 128 return false; 129 } 130 131 if (layers::FenceD3D11::IsSupported(mDevice)) { 132 mFence = layers::FenceD3D11::Create(mDevice); 133 } 134 135 mDevice->GetImmediateContext(getter_AddRefs(mCtx)); 136 if (!mCtx) { 137 aError.Assign("RcANGLE(get immediate context failed)"_ns); 138 return false; 139 } 140 141 // Create DCLayerTree when DirectComposition is used. 142 if (gfx::gfxVars::UseWebRenderDCompWin()) { 143 HWND compositorHwnd = GetCompositorHwnd(); 144 if (compositorHwnd) { 145 mDCLayerTree = DCLayerTree::Create(mGL, mEGLConfig, mDevice, mCtx, 146 compositorHwnd, aError); 147 if (!mDCLayerTree) { 148 return false; 149 } 150 } else { 151 aError.Assign("RcANGLE(no compositor window)"_ns); 152 return false; 153 } 154 } 155 156 // Disable native compositor when fast snapshot is needed. 157 // Taking snapshot of native compositor is very slow on Windows. 158 if (mDCLayerTree && mWidget->GetCompositorOptions().NeedFastSnaphot()) { 159 mDCLayerTree->DisableNativeCompositor(); 160 } 161 162 // Create SwapChain when compositor is not used 163 if (!UseCompositor()) { 164 if (!CreateSwapChain(aError)) { 165 // SwapChain creation failed. 166 return false; 167 } 168 } 169 170 mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(mDevice); 171 if (!mSyncObject->Init()) { 172 // Some errors occur. Clear the mSyncObject here. 173 // Then, there will be no texture synchronization. 174 aError.Assign("RcANGLE(create SyncObject failed)"_ns); 175 return false; 176 } 177 178 InitializeUsePartialPresent(); 179 180 return true; 181 } 182 183 HWND RenderCompositorANGLE::GetCompositorHwnd() { 184 HWND hwnd = 0; 185 186 if (XRE_IsGPUProcess()) { 187 hwnd = mWidget->AsWindows()->GetCompositorHwnd(); 188 } else if ( 189 StaticPrefs:: 190 gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup()) { 191 MOZ_ASSERT(XRE_IsParentProcess()); 192 193 // When GPU process does not exist, we do not need to use compositor window. 194 hwnd = mWidget->AsWindows()->GetHwnd(); 195 } 196 197 return hwnd; 198 } 199 200 bool RenderCompositorANGLE::CreateSwapChainForHWND() { 201 RefPtr<IDXGIFactory> dxgiFactory = DXGIFactory(); 202 RefPtr<IDXGIFactory2> dxgiFactory2; 203 HRESULT hr = dxgiFactory->QueryInterface( 204 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2)); 205 if (FAILED(hr)) { 206 dxgiFactory2 = nullptr; 207 } 208 209 HWND hwnd = mWidget->AsWindows()->GetHwnd(); 210 const bool alpha = ShouldUseAlpha(); 211 if (dxgiFactory2) { 212 RefPtr<IDXGISwapChain1> swapChain1; 213 bool useTripleBuffering = false; 214 215 DXGI_SWAP_CHAIN_DESC1 desc{}; 216 desc.Width = 0; 217 desc.Height = 0; 218 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 219 desc.SampleDesc.Count = 1; 220 desc.SampleDesc.Quality = 0; 221 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 222 bool useFlipSequential = gfx::gfxVars::UseWebRenderFlipSequentialWin(); 223 if (useFlipSequential && !mWidget->AsWindows()->GetCompositorHwnd()) { 224 useFlipSequential = false; 225 gfxCriticalNoteOnce << "FLIP_SEQUENTIAL needs CompositorHwnd. Fallback"; 226 } 227 228 if (useFlipSequential) { 229 useTripleBuffering = gfx::gfxVars::UseWebRenderTripleBufferingWin(); 230 if (useTripleBuffering) { 231 desc.BufferCount = 3; 232 } else { 233 desc.BufferCount = 2; 234 } 235 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 236 desc.Scaling = DXGI_SCALING_NONE; 237 } else { 238 desc.BufferCount = 1; 239 desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; 240 desc.Scaling = DXGI_SCALING_STRETCH; 241 } 242 desc.AlphaMode = 243 alpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE; 244 desc.Flags = 0; 245 246 hr = dxgiFactory2->CreateSwapChainForHwnd( 247 mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(swapChain1)); 248 if (SUCCEEDED(hr) && swapChain1) { 249 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f}; 250 swapChain1->SetBackgroundColor(&color); 251 mSwapChain = swapChain1; 252 mSwapChain1 = swapChain1; 253 mUseTripleBuffering = useTripleBuffering; 254 mSwapChainUsingAlpha = alpha; 255 return true; 256 } 257 if (useFlipSequential) { 258 gfxCriticalNoteOnce << "FLIP_SEQUENTIAL is not supported. Fallback"; 259 } 260 } 261 262 if (mWidget->AsWindows()->GetCompositorHwnd()) { 263 // Destroy compositor window. 264 mWidget->AsWindows()->DestroyCompositorWindow(); 265 hwnd = mWidget->AsWindows()->GetHwnd(); 266 } 267 268 DXGI_SWAP_CHAIN_DESC swapDesc{}; 269 swapDesc.BufferDesc.Width = 0; 270 swapDesc.BufferDesc.Height = 0; 271 swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 272 swapDesc.BufferDesc.RefreshRate.Numerator = 60; 273 swapDesc.BufferDesc.RefreshRate.Denominator = 1; 274 swapDesc.SampleDesc.Count = 1; 275 swapDesc.SampleDesc.Quality = 0; 276 swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 277 swapDesc.BufferCount = 1; 278 swapDesc.OutputWindow = hwnd; 279 swapDesc.Windowed = TRUE; 280 swapDesc.Flags = 0; 281 swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL; 282 283 hr = dxgiFactory->CreateSwapChain(DXGIDevice().get(), &swapDesc, 284 getter_AddRefs(mSwapChain)); 285 if (FAILED(hr)) { 286 return false; 287 } 288 289 RefPtr<IDXGISwapChain1> swapChain1; 290 hr = 291 mSwapChain->QueryInterface((IDXGISwapChain1**)getter_AddRefs(swapChain1)); 292 if (SUCCEEDED(hr)) { 293 mSwapChain1 = std::move(swapChain1); 294 } else { 295 mSwapChain1 = nullptr; 296 } 297 mSwapChainUsingAlpha = alpha; 298 return true; 299 } 300 301 bool RenderCompositorANGLE::CreateSwapChain(nsACString& aError) { 302 MOZ_ASSERT(!UseCompositor()); 303 304 mFirstPresent = true; 305 CreateSwapChainForDCompIfPossible(); 306 if (gfx::gfxVars::UseWebRenderDCompWin() && !mSwapChain) { 307 MOZ_ASSERT(GetCompositorHwnd()); 308 aError.Assign("RcANGLE(create swapchain for dcomp failed)"_ns); 309 return false; 310 } 311 312 if (!mSwapChain && !CreateSwapChainForHWND()) { 313 aError.Assign("RcANGLE(swap chain create failed)"_ns); 314 return false; 315 } 316 317 // We need this because we don't want DXGI to respond to Alt+Enter. 318 HWND hwnd = mWidget->AsWindows()->GetHwnd(); 319 DXGIFactory()->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES); 320 321 if (!ResizeBufferIfNeeded()) { 322 aError.Assign("RcANGLE(resize buffer failed)"_ns); 323 return false; 324 } 325 return true; 326 } 327 328 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible() { 329 if (!mDCLayerTree) { 330 return; 331 } 332 333 HWND hwnd = GetCompositorHwnd(); 334 if (!hwnd) { 335 // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used, 336 // compositor window needs to exist. 337 if (gfx::gfxVars::UseWebRenderDCompWin() || 338 gfx::gfxVars::UseWebRenderFlipSequentialWin()) { 339 gfxCriticalNote << "Compositor window was not created"; 340 } 341 return; 342 } 343 344 // When compositor is enabled, CompositionSurface is used for rendering. 345 // It does not support triple buffering. 346 const bool useTripleBuffering = 347 gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor(); 348 RefPtr<IDXGISwapChain1> swapChain1 = 349 CreateSwapChainForDComp(useTripleBuffering); 350 if (swapChain1) { 351 mSwapChain = swapChain1; 352 mSwapChain1 = swapChain1; 353 mUseTripleBuffering = useTripleBuffering; 354 mDCLayerTree->SetDefaultSwapChain(swapChain1); 355 } else { 356 // Clear DCLayerTree on falire 357 mDCLayerTree = nullptr; 358 } 359 } 360 361 RefPtr<IDXGIDevice> RenderCompositorANGLE::DXGIDevice() { 362 RefPtr<IDXGIDevice> dxgiDevice; 363 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); 364 return dxgiDevice; 365 } 366 367 RefPtr<IDXGIFactory> RenderCompositorANGLE::DXGIFactory() { 368 RefPtr<IDXGIAdapter> adapter; 369 DXGIDevice()->GetAdapter(getter_AddRefs(adapter)); 370 371 RefPtr<IDXGIFactory> dxgiFactory; 372 adapter->GetParent(IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory))); 373 return dxgiFactory; 374 } 375 376 RefPtr<IDXGISwapChain1> RenderCompositorANGLE::CreateSwapChainForDComp( 377 bool aUseTripleBuffering) { 378 RefPtr<IDXGIDevice> dxgiDevice; 379 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); 380 381 RefPtr<IDXGIFactory> dxgiFactory = DXGIFactory(); 382 383 RefPtr<IDXGIFactory2> dxgiFactory2; 384 HRESULT hr = dxgiFactory->QueryInterface( 385 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2)); 386 if (FAILED(hr)) { 387 return nullptr; 388 } 389 390 RefPtr<IDXGISwapChain1> swapChain1; 391 DXGI_SWAP_CHAIN_DESC1 desc{}; 392 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was 393 // set. 394 desc.Width = 1; 395 desc.Height = 1; 396 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 397 desc.SampleDesc.Count = 1; 398 desc.SampleDesc.Quality = 0; 399 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 400 if (aUseTripleBuffering) { 401 desc.BufferCount = 3; 402 } else { 403 desc.BufferCount = 2; 404 } 405 // DXGI_SCALING_NONE caused swap chain creation failure. 406 desc.Scaling = DXGI_SCALING_STRETCH; 407 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; 408 const bool alpha = ShouldUseAlpha(); 409 // See if we need to use transparency. 410 desc.AlphaMode = 411 alpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE; 412 desc.Flags = 0; 413 414 hr = dxgiFactory2->CreateSwapChainForComposition(mDevice, &desc, nullptr, 415 getter_AddRefs(swapChain1)); 416 if (SUCCEEDED(hr) && swapChain1) { 417 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f}; 418 swapChain1->SetBackgroundColor(&color); 419 mSwapChainUsingAlpha = alpha; 420 return swapChain1; 421 } 422 423 return nullptr; 424 } 425 426 bool RenderCompositorANGLE::ShouldUseAlpha() const { 427 return mWidget->AsWindows()->TransparencyModeIs( 428 widget::TransparencyMode::Transparent); 429 } 430 431 bool RenderCompositorANGLE::BeginFrame() { 432 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary(); 433 434 if (!UseCompositor()) { 435 if (NS_WARN_IF(!mSwapChainUsingAlpha && ShouldUseAlpha())) { 436 if (NS_WARN_IF(!RecreateNonNativeCompositorSwapChain())) { 437 return false; 438 } 439 MOZ_ASSERT(mSwapChainUsingAlpha); 440 } 441 if (!ResizeBufferIfNeeded()) { 442 return false; 443 } 444 } 445 446 if (!MakeCurrent()) { 447 gfxCriticalNote << "Failed to make render context current, can't draw."; 448 return false; 449 } 450 451 if (RenderThread::Get()->SyncObjectNeeded() && mSyncObject) { 452 if (!mSyncObject->Synchronize(/* aFallible */ true)) { 453 // It's timeout or other error. Handle the device-reset here. 454 RenderThread::Get()->HandleDeviceReset( 455 gfx::DeviceResetDetectPlace::WR_SYNC_OBJRCT, 456 gfx::DeviceResetReason::UNKNOWN); 457 return false; 458 } 459 } 460 return true; 461 } 462 463 RenderedFrameId RenderCompositorANGLE::EndFrame( 464 const nsTArray<DeviceIntRect>& aDirtyRects) { 465 RenderedFrameId frameId = GetNextRenderFrameId(); 466 InsertGraphicsCommandsFinishedWaitQuery(frameId); 467 468 if (mFence) { 469 mFence->IncrementAndSignal(); 470 } 471 472 if (!UseCompositor()) { 473 auto start = TimeStamp::Now(); 474 if (auto* fxrHandler = mWidget->AsWindows()->GetFxrOutputHandler()) { 475 // There is a Firefox Reality handler for this swapchain. Update this 476 // window's contents to the VR window. 477 if (fxrHandler->TryInitialize(mSwapChain, mDevice)) { 478 fxrHandler->UpdateOutput(mCtx); 479 } 480 } 481 482 const UINT interval = 483 mFirstPresent || 484 StaticPrefs:: 485 gfx_webrender_dcomp_video_swap_chain_present_interval_0() 486 ? 0 487 : 1; 488 const UINT flags = 0; 489 490 const LayoutDeviceIntSize& bufferSize = mBufferSize.ref(); 491 if (mUsePartialPresent && mSwapChain1) { 492 // Clear full render flag. 493 mFullRender = false; 494 // If there is no diry rect, we skip SwapChain present. 495 if (!aDirtyRects.IsEmpty()) { 496 int rectsCount = 0; 497 StackArray<RECT, 1> rects(aDirtyRects.Length()); 498 499 for (size_t i = 0; i < aDirtyRects.Length(); ++i) { 500 const DeviceIntRect& rect = aDirtyRects[i]; 501 // Clip rect to bufferSize 502 int left = std::clamp(rect.min.x, 0, bufferSize.width); 503 int top = std::clamp(rect.min.y, 0, bufferSize.height); 504 int right = std::clamp(rect.max.x, 0, bufferSize.width); 505 int bottom = std::clamp(rect.max.y, 0, bufferSize.height); 506 507 // When rect is not empty, the rect could be passed to Present1(). 508 if (left < right && top < bottom) { 509 rects[rectsCount].left = left; 510 rects[rectsCount].top = top; 511 rects[rectsCount].right = right; 512 rects[rectsCount].bottom = bottom; 513 rectsCount++; 514 } 515 } 516 517 if (rectsCount > 0) { 518 DXGI_PRESENT_PARAMETERS params; 519 PodZero(¶ms); 520 params.DirtyRectsCount = rectsCount; 521 params.pDirtyRects = rects.data(); 522 523 HRESULT hr; 524 hr = mSwapChain1->Present1(interval, flags, ¶ms); 525 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { 526 gfxCriticalNote << "Present1 failed: " << gfx::hexa(hr); 527 mFullRender = true; 528 } 529 } 530 } 531 } else { 532 mSwapChain->Present(interval, flags); 533 } 534 auto end = TimeStamp::Now(); 535 mozilla::glean::gfx::composite_swap_time.AccumulateSingleSample( 536 (end - start).ToMilliseconds() * 10.); 537 538 if (mFirstPresent && mDCLayerTree) { 539 // Wait for the GPU to finish executing its commands before 540 // committing the DirectComposition tree, or else the swapchain 541 // may flicker black when it's first presented. 542 RefPtr<IDXGIDevice2> dxgiDevice2; 543 mDevice->QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice2)); 544 MOZ_ASSERT(dxgiDevice2); 545 546 HANDLE event = ::CreateEvent(nullptr, false, false, nullptr); 547 HRESULT hr = dxgiDevice2->EnqueueSetEvent(event); 548 if (SUCCEEDED(hr)) { 549 DebugOnly<DWORD> result = ::WaitForSingleObject(event, INFINITE); 550 MOZ_ASSERT(result == WAIT_OBJECT_0); 551 } else { 552 gfxCriticalNoteOnce << "EnqueueSetEvent failed: " << gfx::hexa(hr); 553 } 554 ::CloseHandle(event); 555 } 556 mFirstPresent = false; 557 } 558 559 if (mDisablingNativeCompositor) { 560 // During disabling native compositor, we need to wait all gpu tasks 561 // complete. Otherwise, rendering window could cause white flash. 562 WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true); 563 mDisablingNativeCompositor = false; 564 } 565 566 if (mDCLayerTree) { 567 mDCLayerTree->MaybeUpdateDebug(); 568 mDCLayerTree->MaybeCommit(); 569 } 570 571 return frameId; 572 } 573 574 RefPtr<layers::Fence> RenderCompositorANGLE::GetAndResetReleaseFence() { 575 RefPtr<layers::Fence> fence; 576 if (mFence) { 577 fence = mFence->CloneFromHandle(); 578 } 579 return fence.forget(); 580 } 581 582 bool RenderCompositorANGLE::WaitForGPU() { 583 // Note: this waits on the query we inserted in the previous frame, 584 // not the one we just inserted now. Example: 585 // Insert query #1 586 // Present #1 587 // (first frame, no wait) 588 // Insert query #2 589 // Present #2 590 // Wait for query #1. 591 // Insert query #3 592 // Present #3 593 // Wait for query #2. 594 // 595 // This ensures we're done reading textures before swapping buffers. 596 if (!StaticPrefs::gfx_webrender_wait_gpu_finished_disabled_AtStartup()) { 597 return WaitForPreviousGraphicsCommandsFinishedQuery(); 598 } 599 return true; 600 } 601 602 bool RenderCompositorANGLE::ResizeBufferIfNeeded() { 603 MOZ_ASSERT(mSwapChain); 604 605 LayoutDeviceIntSize size = mWidget->GetClientSize(); 606 607 // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set 608 // when DComp is used. 609 size.width = std::max(size.width, 1); 610 size.height = std::max(size.height, 1); 611 612 if (mBufferSize.isSome() && mBufferSize.ref() == size) { 613 MOZ_ASSERT(mEGLSurface); 614 return true; 615 } 616 617 // Release EGLSurface of back buffer before calling ResizeBuffers(). 618 DestroyEGLSurface(); 619 620 mBufferSize = Some(size); 621 622 if (!CreateEGLSurface()) { 623 mBufferSize.reset(); 624 return false; 625 } 626 627 if (mUsePartialPresent) { 628 mFullRender = true; 629 } 630 return true; 631 } 632 633 bool RenderCompositorANGLE::CreateEGLSurface() { 634 MOZ_ASSERT(mBufferSize.isSome()); 635 MOZ_ASSERT(mEGLSurface == EGL_NO_SURFACE); 636 637 HRESULT hr; 638 RefPtr<ID3D11Texture2D> backBuf; 639 640 if (mBufferSize.isNothing()) { 641 gfxCriticalNote << "Buffer size is invalid"; 642 return false; 643 } 644 645 const LayoutDeviceIntSize& size = mBufferSize.ref(); 646 647 // Resize swap chain 648 DXGI_SWAP_CHAIN_DESC desc; 649 hr = mSwapChain->GetDesc(&desc); 650 if (FAILED(hr)) { 651 gfxCriticalNote << "Failed to read swap chain description: " 652 << gfx::hexa(hr) << " Size : " << size; 653 return false; 654 } 655 hr = mSwapChain->ResizeBuffers(desc.BufferCount, size.width, size.height, 656 DXGI_FORMAT_B8G8R8A8_UNORM, 0); 657 if (FAILED(hr)) { 658 gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr) 659 << " Size : " << size; 660 return false; 661 } 662 663 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), 664 (void**)getter_AddRefs(backBuf)); 665 if (hr == DXGI_ERROR_INVALID_CALL) { 666 // This happens on some GPUs/drivers when there's a TDR. 667 if (mDevice->GetDeviceRemovedReason() != S_OK) { 668 gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr) 669 << " Size : " << size; 670 return false; 671 } 672 } 673 674 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, size.width, LOCAL_EGL_HEIGHT, 675 size.height, LOCAL_EGL_NONE}; 676 677 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get()); 678 679 const auto& gle = gl::GLContextEGL::Cast(mGL); 680 const auto& egl = gle->mEgl; 681 const EGLSurface surface = egl->fCreatePbufferFromClientBuffer( 682 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, mEGLConfig, pbuffer_attribs); 683 684 if (!surface) { 685 EGLint err = egl->mLib->fGetError(); 686 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: " 687 << gfx::hexa(err) << " Size : " << size; 688 return false; 689 } 690 691 mEGLSurface = surface; 692 693 return true; 694 } 695 696 void RenderCompositorANGLE::DestroyEGLSurface() { 697 // Release EGLSurface of back buffer before calling ResizeBuffers(). 698 if (mEGLSurface) { 699 const auto& gle = gl::GLContextEGL::Cast(gl()); 700 const auto& egl = gle->mEgl; 701 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE); 702 egl->fDestroySurface(mEGLSurface); 703 mEGLSurface = nullptr; 704 } 705 } 706 707 void RenderCompositorANGLE::Pause() {} 708 709 bool RenderCompositorANGLE::Resume() { return true; } 710 711 void RenderCompositorANGLE::Update() { 712 // Update compositor window's size if it exists. 713 // It needs to be called here, since OS might update compositor 714 // window's size at unexpected timing. 715 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary(); 716 } 717 718 bool RenderCompositorANGLE::MakeCurrent() { 719 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface); 720 return gl()->MakeCurrent(); 721 } 722 723 LayoutDeviceIntSize RenderCompositorANGLE::GetBufferSize() { 724 if (!UseCompositor()) { 725 MOZ_ASSERT(mBufferSize.isSome()); 726 if (mBufferSize.isNothing()) { 727 return LayoutDeviceIntSize(); 728 } 729 return mBufferSize.ref(); 730 } else { 731 auto size = mWidget->GetClientSize(); 732 // This size is used for WR DEBUG_OVERLAY. Its DCTile does not like 0. 733 size.width = std::max(size.width, 1); 734 size.height = std::max(size.height, 1); 735 return size; 736 } 737 } 738 739 RefPtr<ID3D11Query> RenderCompositorANGLE::GetD3D11Query() { 740 RefPtr<ID3D11Query> query; 741 742 if (mRecycledQuery) { 743 query = mRecycledQuery.forget(); 744 return query; 745 } 746 747 CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT); 748 HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(query)); 749 if (FAILED(hr) || !query) { 750 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr); 751 return nullptr; 752 } 753 return query; 754 } 755 756 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery( 757 RenderedFrameId aFrameId) { 758 RefPtr<ID3D11Query> query; 759 query = GetD3D11Query(); 760 if (!query) { 761 return; 762 } 763 764 mCtx->End(query); 765 mCtx->Flush(); 766 mWaitForPresentQueries.emplace(aFrameId, query); 767 } 768 769 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery( 770 bool aWaitAll) { 771 size_t waitLatency = mUseTripleBuffering ? 3 : 2; 772 if (aWaitAll) { 773 waitLatency = 1; 774 } 775 776 while (mWaitForPresentQueries.size() >= waitLatency) { 777 auto queryPair = mWaitForPresentQueries.front(); 778 BOOL result; 779 bool ret = 780 layers::WaitForFrameGPUQuery(mDevice, mCtx, queryPair.second, &result); 781 782 if (!ret) { 783 mWaitForPresentQueries.pop(); 784 return false; 785 } 786 787 // Recycle query for later use. 788 mRecycledQuery = queryPair.second; 789 mLastCompletedFrameId = queryPair.first; 790 mWaitForPresentQueries.pop(); 791 } 792 return true; 793 } 794 795 RenderedFrameId RenderCompositorANGLE::GetLastCompletedFrameId() { 796 while (!mWaitForPresentQueries.empty()) { 797 auto queryPair = mWaitForPresentQueries.front(); 798 if (mCtx->GetData(queryPair.second, nullptr, 0, 799 D3D11_ASYNC_GETDATA_DONOTFLUSH) != S_OK) { 800 break; 801 } 802 803 mRecycledQuery = queryPair.second; 804 mLastCompletedFrameId = queryPair.first; 805 mWaitForPresentQueries.pop(); 806 } 807 808 nsPrintfCString marker("Pending frames %u", 809 (uint32_t)mWaitForPresentQueries.size()); 810 PROFILER_MARKER_TEXT("GetLastCompletedFrameId", GRAPHICS, {}, marker); 811 812 return mLastCompletedFrameId; 813 } 814 815 RenderedFrameId RenderCompositorANGLE::UpdateFrameId() { 816 RenderedFrameId frameId = GetNextRenderFrameId(); 817 InsertGraphicsCommandsFinishedWaitQuery(frameId); 818 return frameId; 819 } 820 821 gfx::DeviceResetReason RenderCompositorANGLE::IsContextLost(bool aForce) { 822 // glGetGraphicsResetStatus does not always work to detect timeout detection 823 // and recovery (TDR). On Windows, ANGLE itself is just relying upon the same 824 // API, so we should not need to check it separately. 825 auto reason = mDevice->GetDeviceRemovedReason(); 826 return layers::DXGIErrorToDeviceResetReason(reason); 827 } 828 829 bool RenderCompositorANGLE::UseCompositor() const { 830 return mDCLayerTree && mDCLayerTree->UseNativeCompositor(); 831 } 832 833 bool RenderCompositorANGLE::UseLayerCompositor() const { 834 return mDCLayerTree && mDCLayerTree->UseLayerCompositor(); 835 } 836 837 bool RenderCompositorANGLE::SupportAsyncScreenshot() { 838 return !UseCompositor() && !mDisablingNativeCompositor; 839 } 840 841 bool RenderCompositorANGLE::ShouldUseNativeCompositor() { 842 return UseCompositor(); 843 } 844 845 bool RenderCompositorANGLE::ShouldUseLayerCompositor() const { 846 return UseLayerCompositor(); 847 } 848 849 void RenderCompositorANGLE::CompositorBeginFrame() { 850 mDCLayerTree->CompositorBeginFrame(); 851 } 852 853 void RenderCompositorANGLE::CompositorEndFrame() { 854 mDCLayerTree->CompositorEndFrame(); 855 } 856 857 void RenderCompositorANGLE::Bind(wr::NativeTileId aId, 858 wr::DeviceIntPoint* aOffset, uint32_t* aFboId, 859 wr::DeviceIntRect aDirtyRect, 860 wr::DeviceIntRect aValidRect) { 861 mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect, aValidRect); 862 } 863 864 void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); } 865 866 void RenderCompositorANGLE::BindSwapChain(wr::NativeSurfaceId aId, 867 const wr::DeviceIntRect* aDirtyRects, 868 size_t aNumDirtyRects) { 869 mDCLayerTree->BindSwapChain(aId, aDirtyRects, aNumDirtyRects); 870 } 871 void RenderCompositorANGLE::PresentSwapChain( 872 wr::NativeSurfaceId aId, const wr::DeviceIntRect* aDirtyRects, 873 size_t aNumDirtyRects) { 874 mDCLayerTree->PresentSwapChain(aId, aDirtyRects, aNumDirtyRects); 875 } 876 877 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId, 878 wr::DeviceIntPoint aVirtualOffset, 879 wr::DeviceIntSize aTileSize, 880 bool aIsOpaque) { 881 mDCLayerTree->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque); 882 } 883 884 void RenderCompositorANGLE::CreateSwapChainSurface(wr::NativeSurfaceId aId, 885 wr::DeviceIntSize aSize, 886 bool aIsOpaque, 887 bool aNeedsSyncDcompCommit) { 888 mDCLayerTree->CreateSwapChainSurface(aId, aSize, aIsOpaque, 889 aNeedsSyncDcompCommit); 890 } 891 892 void RenderCompositorANGLE::ResizeSwapChainSurface(wr::NativeSurfaceId aId, 893 wr::DeviceIntSize aSize) { 894 mDCLayerTree->ResizeSwapChainSurface(aId, aSize); 895 } 896 897 void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId, 898 bool aIsOpaque) { 899 mDCLayerTree->CreateExternalSurface(aId, aIsOpaque); 900 } 901 902 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) { 903 mDCLayerTree->DestroySurface(aId); 904 } 905 906 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX, 907 int aY) { 908 mDCLayerTree->CreateTile(aId, aX, aY); 909 } 910 911 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX, 912 int aY) { 913 mDCLayerTree->DestroyTile(aId, aX, aY); 914 } 915 916 void RenderCompositorANGLE::AttachExternalImage( 917 wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) { 918 mDCLayerTree->AttachExternalImage(aId, aExternalImage); 919 } 920 921 void RenderCompositorANGLE::AddSurface( 922 wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform, 923 wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering, 924 wr::DeviceIntRect aRoundedClipRect, wr::ClipRadius aClipRadius) { 925 mDCLayerTree->AddSurface(aId, aTransform, aClipRect, aImageRendering, 926 aRoundedClipRect, aClipRadius); 927 } 928 929 void RenderCompositorANGLE::GetCompositorCapabilities( 930 CompositorCapabilities* aCaps) { 931 RenderCompositor::GetCompositorCapabilities(aCaps); 932 933 if (StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup()) { 934 aCaps->virtual_surface_size = VIRTUAL_SURFACE_SIZE; 935 } else { 936 aCaps->virtual_surface_size = 0; 937 } 938 // DComp video overlay does not support negative scaling. See Bug 1831820 939 aCaps->supports_external_compositor_surface_negative_scaling = false; 940 } 941 942 void RenderCompositorANGLE::GetWindowProperties(WindowProperties* aProperties) { 943 aProperties->is_opaque = !ShouldUseAlpha(); 944 const bool enable_screenshot = 945 mDCLayerTree && mDCLayerTree->GetAsyncScreenshotEnabled(); 946 aProperties->enable_screenshot = enable_screenshot; 947 } 948 949 void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable) { 950 // XXX Re-enable native compositor is not handled yet. 951 MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor); 952 MOZ_RELEASE_ASSERT(!aEnable); 953 LOG("RenderCompositorANGLE::EnableNativeCompositor() aEnable %d", aEnable); 954 955 if (!UseCompositor()) { 956 return; 957 } 958 959 mDCLayerTree->DisableNativeCompositor(); 960 961 if (!RecreateNonNativeCompositorSwapChain()) { 962 gfxCriticalNote << "Failed to re-create SwapChain"; 963 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); 964 return; 965 } 966 967 mDisablingNativeCompositor = true; 968 } 969 970 bool RenderCompositorANGLE::EnableAsyncScreenshot() { 971 if (!UseLayerCompositor()) { 972 return false; 973 } 974 return mDCLayerTree->EnableAsyncScreenshot(); 975 } 976 977 bool RenderCompositorANGLE::RecreateNonNativeCompositorSwapChain() { 978 DestroyEGLSurface(); 979 mBufferSize.reset(); 980 981 if (mDCLayerTree) { 982 RefPtr<IDXGISwapChain1> swapChain1 = 983 CreateSwapChainForDComp(mUseTripleBuffering); 984 if (!swapChain1) { 985 return false; 986 } 987 mSwapChain = swapChain1; 988 mSwapChain1 = swapChain1; 989 mDCLayerTree->SetDefaultSwapChain(swapChain1); 990 } else { 991 if (NS_WARN_IF(!CreateSwapChainForHWND())) { 992 return false; 993 } 994 } 995 return ResizeBufferIfNeeded(); 996 } 997 998 void RenderCompositorANGLE::InitializeUsePartialPresent() { 999 // Even when mSwapChain1 is null, we could enable WR partial present, since 1000 // when mSwapChain1 is null, SwapChain is blit model swap chain with one 1001 // buffer. 1002 mUsePartialPresent = !UseCompositor() && 1003 !mWidget->AsWindows()->HasFxrOutputHandler() && 1004 gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0; 1005 } 1006 1007 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; } 1008 1009 bool RenderCompositorANGLE::RequestFullRender() { 1010 // XXX Remove when partial update is supported. 1011 if (UseLayerCompositor() && mDCLayerTree->UseDCLayerDCompositionTexture()) { 1012 return true; 1013 } 1014 return mFullRender; 1015 } 1016 1017 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() { 1018 if (!mUsePartialPresent) { 1019 return 0; 1020 } 1021 return gfx::gfxVars::WebRenderMaxPartialPresentRects(); 1022 } 1023 1024 bool RenderCompositorANGLE::MaybeReadback( 1025 const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat, 1026 const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) { 1027 MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8); 1028 1029 if (!UseCompositor()) { 1030 return false; 1031 } 1032 1033 auto start = TimeStamp::Now(); 1034 1035 HDC nulldc = ::GetDC(NULL); 1036 HDC dc = ::CreateCompatibleDC(nulldc); 1037 ::ReleaseDC(nullptr, nulldc); 1038 if (!dc) { 1039 gfxCriticalError() << "CreateCompatibleDC failed"; 1040 return false; 1041 } 1042 1043 BITMAPV4HEADER header; 1044 memset(&header, 0, sizeof(BITMAPV4HEADER)); 1045 header.bV4Size = sizeof(BITMAPV4HEADER); 1046 header.bV4Width = aReadbackSize.width; 1047 header.bV4Height = -LONG(aReadbackSize.height); // top-to-buttom DIB 1048 header.bV4Planes = 1; 1049 header.bV4BitCount = 32; 1050 header.bV4V4Compression = BI_BITFIELDS; 1051 header.bV4RedMask = 0x00FF0000; 1052 header.bV4GreenMask = 0x0000FF00; 1053 header.bV4BlueMask = 0x000000FF; 1054 header.bV4AlphaMask = 0xFF000000; 1055 1056 void* readbackBits = nullptr; 1057 HBITMAP bitmap = 1058 ::CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&header), 1059 DIB_RGB_COLORS, &readbackBits, nullptr, 0); 1060 if (!bitmap) { 1061 ::DeleteDC(dc); 1062 gfxCriticalError() << "CreateDIBSection failed"; 1063 return false; 1064 } 1065 1066 ::SelectObject(dc, bitmap); 1067 1068 UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT; 1069 HWND hwnd = mWidget->AsWindows()->GetHwnd(); 1070 1071 mDCLayerTree->WaitForCommitCompletion(); 1072 1073 BOOL result = ::PrintWindow(hwnd, dc, flags); 1074 if (!result) { 1075 ::DeleteObject(bitmap); 1076 ::DeleteDC(dc); 1077 gfxCriticalError() << "PrintWindow failed"; 1078 return false; 1079 } 1080 1081 ::GdiFlush(); 1082 1083 memcpy(&aReadbackBuffer[0], readbackBits, aReadbackBuffer.length()); 1084 1085 ::DeleteObject(bitmap); 1086 ::DeleteDC(dc); 1087 1088 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds()); 1089 if (latencyMs > 500) { 1090 gfxCriticalNote << "Readback took too long: " << latencyMs << " ms"; 1091 } 1092 1093 if (aNeedsYFlip) { 1094 *aNeedsYFlip = false; 1095 } 1096 1097 return true; 1098 } 1099 1100 } // namespace mozilla::wr