tor-browser

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

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(&params);
   1983      params.DirtyRectsCount = rectsCount;
   1984      params.pDirtyRects = rects.data();
   1985 
   1986      hr = mSwapChain->Present1(0, flags, &params);
   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, &currentFboId);
   3130  gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
   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