tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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(&params);
    520          params.DirtyRectsCount = rectsCount;
    521          params.pDirtyRects = rects.data();
    522 
    523          HRESULT hr;
    524          hr = mSwapChain1->Present1(interval, flags, &params);
    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