Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  gfxWindowsPlatform.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#define INITGUID  // set before devguid.h

#include "gfxWindowsPlatform.h"

#include "cairo.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/layers/CompositorBridgeChild.h"

#include "gfxBlur.h"
#include "gfxImageSurface.h"
#include "gfxWindowsSurface.h"

#include "nsUnicharUtils.h"
#include "nsUnicodeProperties.h"

#include "mozilla/Preferences.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ProfilerThreadSleep.h"
#include "mozilla/Components.h"
#include "mozilla/Sprintf.h"
#include "mozilla/WindowsVersion.h"
#include "nsIGfxInfo.h"
#include "nsServiceManagerUtils.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "mozilla/glean/GfxMetrics.h"
#include "mozilla/Telemetry.h"

#include "plbase64.h"
#include "nsIXULRuntime.h"
#include "imgLoader.h"

#include "nsIGfxInfo.h"

#include "gfxCrashReporterUtils.h"

#include "gfxGDIFontList.h"
#include "gfxGDIFont.h"

#include "mozilla/layers/CanvasChild.h"
#include "mozilla/layers/CompositorThread.h"

#include "gfxDWriteFontList.h"
#include "gfxDWriteFonts.h"
#include "gfxDWriteCommon.h"
#include <dwrite.h>

#include "gfxTextRun.h"
#include "gfxUserFontSet.h"
#include "nsWindowsHelpers.h"
#include "gfx2DGlue.h"

#include <string>

#include <d3d10_1.h>

#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/gfxVars.h"

#include <dwmapi.h>
#include <d3d11.h>
#include <d2d1_1.h>

#include "nsIMemoryReporter.h"
#include <winternl.h>
#include "d3dkmtQueryStatistics.h"

#include "base/thread.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "gfxConfig.h"
#include "VsyncSource.h"
#include "DriverCrashGuard.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/gfx/DeviceManagerDx.h"
#include "mozilla/gfx/DisplayConfigWindows.h"
#include "mozilla/layers/DeviceAttachmentsD3D11.h"
#include "mozilla/WindowsProcessMitigations.h"
#include "D3D11Checks.h"
#include "mozilla/ScreenHelperWin.h"

using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::widget;
using namespace mozilla::image;
using namespace mozilla::unicode;

DCForMetrics::DCForMetrics() {
  // Get the whole screen DC:
  mDC = GetDC(nullptr);
  SetGraphicsMode(mDC, GM_ADVANCED);
}

class GfxD2DVramReporter final : public nsIMemoryReporter {
  ~GfxD2DVramReporter() {}

 public:
  NS_DECL_ISUPPORTS

  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                            nsISupports* aData, bool aAnonymize) override {
    MOZ_COLLECT_REPORT("gfx-d2d-vram-draw-target", KIND_OTHER, UNITS_BYTES,
                       Factory::GetD2DVRAMUsageDrawTarget(),
                       "Video memory used by D2D DrawTargets.");

    MOZ_COLLECT_REPORT("gfx-d2d-vram-source-surface", KIND_OTHER, UNITS_BYTES,
                       Factory::GetD2DVRAMUsageSourceSurface(),
                       "Video memory used by D2D SourceSurfaces.");

    return NS_OK;
  }
};

NS_IMPL_ISUPPORTS(GfxD2DVramReporter, nsIMemoryReporter)

class GPUAdapterReporter final : public nsIMemoryReporter {
  // Callers must Release the DXGIAdapter after use or risk mem-leak
  static bool GetDXGIAdapter(IDXGIAdapter** aDXGIAdapter) {
    RefPtr<ID3D11Device> d3d11Device;
    RefPtr<IDXGIDevice> dxgiDevice;
    bool result = false;

    if ((d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) {
      if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice),
                                      getter_AddRefs(dxgiDevice)) == S_OK) {
        result = (dxgiDevice->GetAdapter(aDXGIAdapter) == S_OK);
      }
    }

    return result;
  }

  ~GPUAdapterReporter() {}

 public:
  NS_DECL_ISUPPORTS

  NS_IMETHOD
  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
                 bool aAnonymize) override {
    HANDLE ProcessHandle = GetCurrentProcess();

    int64_t dedicatedBytesUsed = 0;
    int64_t sharedBytesUsed = 0;
    int64_t committedBytesUsed = 0;
    IDXGIAdapter* DXGIAdapter;

    HMODULE gdi32Handle;
    PFND3DKMTQS queryD3DKMTStatistics = nullptr;

    if ((gdi32Handle = LoadLibrary(TEXT("gdi32.dll"))))
      queryD3DKMTStatistics =
          (PFND3DKMTQS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");

    if (queryD3DKMTStatistics && GetDXGIAdapter(&DXGIAdapter)) {
      // Most of this block is understood thanks to wj32's work on Process
      // Hacker

      DXGI_ADAPTER_DESC adapterDesc;
      D3DKMTQS queryStatistics;

      DXGIAdapter->GetDesc(&adapterDesc);
      DXGIAdapter->Release();

      memset(&queryStatistics, 0, sizeof(D3DKMTQS));
      queryStatistics.Type = D3DKMTQS_PROCESS;
      queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
      queryStatistics.hProcess = ProcessHandle;
      if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
        committedBytesUsed =
            queryStatistics.QueryResult.ProcessInfo.SystemMemory.BytesAllocated;
      }

      memset(&queryStatistics, 0, sizeof(D3DKMTQS));
      queryStatistics.Type = D3DKMTQS_ADAPTER;
      queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
      if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
        ULONG i;
        ULONG segmentCount = queryStatistics.QueryResult.AdapterInfo.NbSegments;

        for (i = 0; i < segmentCount; i++) {
          memset(&queryStatistics, 0, sizeof(D3DKMTQS));
          queryStatistics.Type = D3DKMTQS_SEGMENT;
          queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
          queryStatistics.QuerySegment.SegmentId = i;

          if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
            bool aperture = queryStatistics.QueryResult.SegmentInfo.Aperture;
            memset(&queryStatistics, 0, sizeof(D3DKMTQS));
            queryStatistics.Type = D3DKMTQS_PROCESS_SEGMENT;
            queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
            queryStatistics.hProcess = ProcessHandle;
            queryStatistics.QueryProcessSegment.SegmentId = i;
            if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
              ULONGLONG bytesCommitted =
                  queryStatistics.QueryResult.ProcessSegmentInfo.BytesCommitted;
              if (aperture)
                sharedBytesUsed += bytesCommitted;
              else
                dedicatedBytesUsed += bytesCommitted;
            }
          }
        }
      }
    }

    FreeLibrary(gdi32Handle);

    MOZ_COLLECT_REPORT("gpu-committed", KIND_OTHER, UNITS_BYTES,
                       committedBytesUsed,
                       "Memory committed by the Windows graphics system.");

    MOZ_COLLECT_REPORT(
        "gpu-dedicated", KIND_OTHER, UNITS_BYTES, dedicatedBytesUsed,
        "Out-of-process memory allocated for this process in a physical "
        "GPU adapter's memory.");

    MOZ_COLLECT_REPORT("gpu-shared", KIND_OTHER, UNITS_BYTES, sharedBytesUsed,
                       "In-process memory that is shared with the GPU.");

    return NS_OK;
  }
};

NS_IMPL_ISUPPORTS(GPUAdapterReporter, nsIMemoryReporter)

Atomic<size_t> gfxWindowsPlatform::sD3D11SharedTextures;
Atomic<size_t> gfxWindowsPlatform::sD3D9SharedTextures;

class D3DSharedTexturesReporter final : public nsIMemoryReporter {
  ~D3DSharedTexturesReporter() {}

 public:
  NS_DECL_ISUPPORTS

  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                            nsISupports* aData, bool aAnonymize) override {
    if (gfxWindowsPlatform::sD3D11SharedTextures > 0) {
      MOZ_COLLECT_REPORT("d3d11-shared-textures", KIND_OTHER, UNITS_BYTES,
                         gfxWindowsPlatform::sD3D11SharedTextures,
                         "D3D11 shared textures.");
    }

    if (gfxWindowsPlatform::sD3D9SharedTextures > 0) {
      MOZ_COLLECT_REPORT("d3d9-shared-textures", KIND_OTHER, UNITS_BYTES,
                         gfxWindowsPlatform::sD3D9SharedTextures,
                         "D3D9 shared textures.");
    }

    return NS_OK;
  }
};

NS_IMPL_ISUPPORTS(D3DSharedTexturesReporter, nsIMemoryReporter)

gfxWindowsPlatform::gfxWindowsPlatform() : mRenderMode(RENDER_GDI) {
  // If win32k is locked down then we can't use COM STA and shouldn't need it.
  // Also, we won't be using any GPU memory in this process.
  if (!IsWin32kLockedDown()) {
    /*
     * Initialize COM
     */

    CoInitialize(nullptr);

    RegisterStrongMemoryReporter(new GfxD2DVramReporter());
    RegisterStrongMemoryReporter(new GPUAdapterReporter());
    RegisterStrongMemoryReporter(new D3DSharedTexturesReporter());
  }
}

gfxWindowsPlatform::~gfxWindowsPlatform() {
  mozilla::gfx::Factory::D2DCleanup();

  DeviceManagerDx::Shutdown();

  // We don't initialize COM when win32k is locked down.
  if (!IsWin32kLockedDown()) {
    /*
     * Uninitialize COM
     */

    CoUninitialize();
  }
}

/* static */
void gfxWindowsPlatform::InitMemoryReportersForGPUProcess() {
  MOZ_RELEASE_ASSERT(XRE_IsGPUProcess());

  RegisterStrongMemoryReporter(new GfxD2DVramReporter());
  RegisterStrongMemoryReporter(new GPUAdapterReporter());
  RegisterStrongMemoryReporter(new D3DSharedTexturesReporter());
}

/* static */
nsresult gfxWindowsPlatform::GetGpuTimeSinceProcessStartInMs(
    uint64_t* aResult) {
  // If win32k is locked down then we should not have any GPU processing and
  // cannot use these APIs either way.
  if (IsWin32kLockedDown()) {
    *aResult = 0;
    return NS_OK;
  }

  nsModuleHandle module(LoadLibrary(L"gdi32.dll"));
  if (!module) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  PFND3DKMTQS queryD3DKMTStatistics =
      (PFND3DKMTQS)GetProcAddress(module, "D3DKMTQueryStatistics");
  if (!queryD3DKMTStatistics) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  gfx::DeviceManagerDx* dm = DeviceManagerDx::Get();
  if (!dm) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  D3D11DeviceStatus status;
  if (!dm->ExportDeviceInfo(&status)) {
    // Assume that we used 0ms of GPU time if the device manager
    // doesn't know the device status.
    *aResult = 0;
    return NS_OK;
  }

  const DxgiAdapterDesc& adapterDesc = status.adapter();

  D3DKMTQS queryStatistics;
  memset(&queryStatistics, 0, sizeof(D3DKMTQS));
  queryStatistics.Type = D3DKMTQS_ADAPTER;
  queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
  if (!NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
    return NS_ERROR_FAILURE;
  }

  uint64_t result = 0;
  ULONG nodeCount = queryStatistics.QueryResult.AdapterInfo.NodeCount;
  for (ULONG i = 0; i < nodeCount; ++i) {
    memset(&queryStatistics, 0, sizeof(D3DKMTQS));
    queryStatistics.Type = D3DKMTQS_PROCESS_NODE;
    queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
    queryStatistics.hProcess = GetCurrentProcess();
    queryStatistics.QueryProcessNode.NodeId = i;
    if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
      result += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime
                    .QuadPart *
                100 / PR_NSEC_PER_MSEC;
    }
  }

  *aResult = result;
  return NS_OK;
}

static void UpdateANGLEConfig() {
  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    gfxConfig::Disable(Feature::D3D11_HW_ANGLE, FeatureStatus::Disabled,
                       "D3D11 compositing is disabled",
                       "FEATURE_FAILURE_HW_ANGLE_D3D11_DISABLED"_ns);
  }
}

void gfxWindowsPlatform::InitAcceleration() {
  gfxPlatform::InitAcceleration();

  DeviceManagerDx::Init();

  InitializeConfig();
  InitGPUProcessSupport();
  // Ensure devices initialization. SharedSurfaceANGLE and
  // SharedSurfaceD3D11Interop use them. The devices are lazily initialized
  // with WebRender to reduce memory usage.
  // Initialize them now when running non-e10s.
  if (!BrowserTabsRemoteAutostart()) {
    EnsureDevicesInitialized();
  }
  UpdateANGLEConfig();
  UpdateRenderMode();

  // If we have Skia and we didn't init dwrite already, do it now.
  if (!DWriteEnabled() && GetDefaultContentBackend() == BackendType::SKIA) {
    InitDWriteSupport();
  }
  // We need to listen for font setting changes even if DWrite is not used.
  Factory::SetSystemTextQuality(gfxVars::SystemTextQuality());
  gfxVars::SetSystemTextQualityListener(
      gfxDWriteFont::SystemTextQualityChanged);

  // CanUseHardwareVideoDecoding depends on DeviceManagerDx state,
  // so update the cached value now.
  UpdateCanUseHardwareVideoDecoding();

  // Our ScreenHelperWin also depends on DeviceManagerDx state.
  if (XRE_IsParentProcess() && !gfxPlatform::IsHeadless()) {
    ScreenHelperWin::RefreshScreens();
  }

  RecordStartupTelemetry();
}

void gfxWindowsPlatform::InitWebRenderConfig() {
  gfxPlatform::InitWebRenderConfig();
  UpdateBackendPrefs();
}

bool gfxWindowsPlatform::CanUseHardwareVideoDecoding() {
  DeviceManagerDx* dm = DeviceManagerDx::Get();
  if (!dm) {
    return false;
  }
  if (!dm->TextureSharingWorks()) {
    return false;
  }
  return !dm->IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
}

bool gfxWindowsPlatform::InitDWriteSupport() {
  mozilla::ScopedGfxFeatureReporter reporter("DWrite");
  if (!gfxDWriteFont::InitDWriteSupport()) {
    return false;
  }

  reporter.SetSuccessful();
  return true;
}

bool gfxWindowsPlatform::HandleDeviceReset() {
  mozilla::gfx::DeviceResetReason resetReason =
      mozilla::gfx::DeviceResetReason::OK;
  if (!DidRenderingDeviceReset(&resetReason)) {
    return false;
  }

  if (resetReason != mozilla::gfx::DeviceResetReason::FORCED_RESET) {
    Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON,
                          uint32_t(resetReason));
  }

  // Remove devices and adapters.
  DeviceManagerDx::Get()->ResetDevices();

  imgLoader::NormalLoader()->ClearCache(true);
  imgLoader::NormalLoader()->ClearCache(false);
  imgLoader::PrivateBrowsingLoader()->ClearCache(true);
  imgLoader::PrivateBrowsingLoader()->ClearCache(false);
  gfxAlphaBoxBlur::ShutdownBlurCache();

  gfxConfig::Reset(Feature::D3D11_COMPOSITING);
  gfxConfig::Reset(Feature::D3D11_HW_ANGLE);
  gfxConfig::Reset(Feature::DIRECT2D);

  InitializeConfig();
  // XXX Add InitWebRenderConfig() calling.
  if (mInitializedDevices) {
    InitGPUProcessSupport();
    InitializeDevices();
  }
  UpdateANGLEConfig();
  return true;
}

BackendPrefsData gfxWindowsPlatform::GetBackendPrefs() const {
  BackendPrefsData data;

  data.mCanvasBitmask = BackendTypeBit(BackendType::SKIA);
  data.mContentBitmask = BackendTypeBit(BackendType::SKIA);
  data.mCanvasDefault = BackendType::SKIA;
  data.mContentDefault = BackendType::SKIA;

  if (gfxConfig::IsEnabled(Feature::DIRECT2D)) {
    data.mCanvasBitmask |= BackendTypeBit(BackendType::DIRECT2D1_1);
    data.mCanvasDefault = BackendType::DIRECT2D1_1;
  }
  return data;
}

void gfxWindowsPlatform::UpdateBackendPrefs() {
  BackendPrefsData data = GetBackendPrefs();
  // Remove DIRECT2D1 preference if D2D1Device does not exist.
  if (!Factory::HasD2D1Device()) {
    data.mContentBitmask &= ~BackendTypeBit(BackendType::DIRECT2D1_1);
    if (data.mContentDefault == BackendType::DIRECT2D1_1) {
      data.mContentDefault = BackendType::SKIA;
    }

    // Don't exclude DIRECT2D1_1 if using remote canvas, because DIRECT2D1_1 and
    // hence the device will be used in the GPU process.
    if (!gfxPlatform::UseRemoteCanvas()) {
      data.mCanvasBitmask &= ~BackendTypeBit(BackendType::DIRECT2D1_1);
      if (data.mCanvasDefault == BackendType::DIRECT2D1_1) {
        data.mCanvasDefault = BackendType::SKIA;
      }
    }
  }
  InitBackendPrefs(std::move(data));
}

bool gfxWindowsPlatform::IsDirect2DBackend() {
  return GetDefaultContentBackend() == BackendType::DIRECT2D1_1;
}

void gfxWindowsPlatform::UpdateRenderMode() {
  bool didReset = HandleDeviceReset();

  UpdateBackendPrefs();

  if (didReset) {
    mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(
        IntSize(1, 1), SurfaceFormat::B8G8R8A8);
    if (!mScreenReferenceDrawTarget) {
      gfxCriticalNote
          << "Failed to update reference draw target after device reset"
          << ", D3D11 device:" << hexa(Factory::GetDirect3D11Device().get())
          << ", D3D11 status:"
          << FeatureStatusToString(
                 gfxConfig::GetValue(Feature::D3D11_COMPOSITING))
          << ", D2D1 device:" << hexa(Factory::GetD2D1Device().get())
          << ", D2D1 status:"
          << FeatureStatusToString(gfxConfig::GetValue(Feature::DIRECT2D))
          << ", content:" << int(GetDefaultContentBackend())
          << ", compositor:" << int(GetCompositorBackend());
      MOZ_CRASH(
          "GFX: Failed to update reference draw target after device reset");
    }
  }
}

mozilla::gfx::BackendType gfxWindowsPlatform::GetContentBackendFor(
    mozilla::layers::LayersBackend aLayers) {
  mozilla::gfx::BackendType defaultBackend =
      gfxPlatform::GetDefaultContentBackend();
  if (aLayers == LayersBackend::LAYERS_WR &&
      gfx::gfxVars::UseWebRenderANGLE()) {
    return defaultBackend;
  }

  if (defaultBackend == BackendType::DIRECT2D1_1) {
    // We can't have D2D without D3D11 layers, so fallback to Skia.
    return BackendType::SKIA;
  }

  // Otherwise we have some non-accelerated backend and that's ok.
  return defaultBackend;
}

mozilla::gfx::BackendType gfxWindowsPlatform::GetPreferredCanvasBackend() {
  mozilla::gfx::BackendType backend = gfxPlatform::GetPreferredCanvasBackend();

  if (backend == BackendType::DIRECT2D1_1) {
    if (!gfx::gfxVars::UseWebRenderANGLE()) {
      // We can't have D2D without ANGLE when WebRender is enabled, so fallback
      // to Skia.
      return BackendType::SKIA;
    }

    // Fall back to software when remote canvas has been deactivated.
    if (CanvasChild::Deactivated()) {
      return BackendType::SKIA;
    }
  }
  return backend;
}

bool gfxWindowsPlatform::CreatePlatformFontList() {
  if (DWriteEnabled()) {
    if (gfxPlatformFontList::Initialize(new gfxDWriteFontList)) {
      return true;
    }

    // DWrite font initialization failed! Don't know why this would happen,
    // but apparently it can - see bug 594865.
    // So we're going to fall back to GDI fonts & rendering.
    DisableD2D(FeatureStatus::Failed, "Failed to initialize fonts",
               "FEATURE_FAILURE_FONT_FAIL"_ns);
  }

  // Make sure the static variable is initialized...
  gfxPlatform::HasVariationFontSupport();
  // ...then force it to false, even if the Windows version was recent enough
  // to permit it, as we're using GDI fonts.
  sHasVariationFontSupport = false;

  return gfxPlatformFontList::Initialize(new gfxGDIFontList);
}

// This function will permanently disable D2D for the session. It's intended to
// be used when, after initially chosing to use Direct2D, we encounter a
// scenario we can't support.
//
// This is called during gfxPlatform::Init() so at this point there should be no
// DrawTargetD2D/1 instances.
void gfxWindowsPlatform::DisableD2D(FeatureStatus aStatus, const char* aMessage,
                                    const nsACString& aFailureId) {
  gfxConfig::SetFailed(Feature::DIRECT2D, aStatus, aMessage, aFailureId);
  Factory::SetDirect3D11Device(nullptr);
  UpdateBackendPrefs();
}

already_AddRefed<gfxASurface> gfxWindowsPlatform::CreateOffscreenSurface(
    const IntSize& aSize, gfxImageFormat aFormat) {
  if (!Factory::AllowedSurfaceSize(aSize)) {
    return nullptr;
  }

  RefPtr<gfxASurface> surf = nullptr;

#ifdef CAIRO_HAS_WIN32_SURFACE
  if (!XRE_IsContentProcess()) {
    if (mRenderMode == RENDER_GDI || mRenderMode == RENDER_DIRECT2D) {
      surf = new gfxWindowsSurface(aSize, aFormat);
    }
  }
#endif

  if (!surf || surf->CairoStatus()) {
    surf = new gfxImageSurface(aSize, aFormat);
  }

  return surf.forget();
}

void gfxWindowsPlatform::GetCommonFallbackFonts(
    uint32_t aCh, Script aRunScript, eFontPresentation aPresentation,
    nsTArray<const char*>& aFontList) {
  if (PrefersColor(aPresentation)) {
    aFontList.AppendElement("Segoe UI Emoji");
    aFontList.AppendElement("Twemoji Mozilla");
  }

  switch (aRunScript) {
    case Script::INVALID:
    case Script::NUM_SCRIPT_CODES:
      // Ensure the switch covers all the Script enum values.
      MOZ_ASSERT_UNREACHABLE("bad script code");
      break;

    case Script::COMMON:
    case Script::INHERITED:
      // In most cases, COMMON and INHERITED characters will be merged into
      // their context, but if they occur without context, we'll just treat
      // them like Latin, etc.
    case Script::LATIN:
    case Script::CYRILLIC:
    case Script::GREEK:
    case Script::ARMENIAN:
    case Script::HEBREW:
      // We always append Arial below, so no need to add it here.
      // aFontList.AppendElement("Arial");
      break;

    case Script::MATHEMATICAL_NOTATION:
    case Script::SYMBOLS:
    case Script::SYMBOLS_EMOJI:
      // Not currently returned by script run resolution (but some symbols may
      // be handled below).
      break;

      // CJK-related script codes are a bit troublesome because of unification;
      // we'll probably just get HAN much of the time, so the choice of which
      // language font to try for fallback is rather arbitrary. Usually, though,
      // we hope that font prefs will have handled this earlier.
    case Script::BOPOMOFO:
    case Script::HAN_WITH_BOPOMOFO:
    case Script::SIMPLIFIED_HAN:
    case Script::HAN:
      aFontList.AppendElement("SimSun");
      if (aCh > 0xFFFF) {
        aFontList.AppendElement("SimSun-ExtB");
      }
      break;
      // Currently, we don't resolve script runs to this value, but we may do so
      // in future if we get better at handling things like `lang=zh-Hant`, not
      // just resolving based on the Unicode text.
    case Script::TRADITIONAL_HAN:
      aFontList.AppendElement("MingLiU");
      if (aCh > 0xFFFF) {
        aFontList.AppendElement("MingLiU-ExtB");
      }
      break;
    case Script::HIRAGANA:
    case Script::KATAKANA:
    case Script::KATAKANA_OR_HIRAGANA:
    case Script::JAPANESE:
      aFontList.AppendElement("Yu Gothic");
      aFontList.AppendElement("MS PGothic");
      break;
    case Script::HANGUL:
    case Script::JAMO:
    case Script::KOREAN:
      aFontList.AppendElement("Malgun Gothic");
      break;

    case Script::YI:
      aFontList.AppendElement("Microsoft Yi Baiti");
      break;
    case Script::MONGOLIAN:
      aFontList.AppendElement("Mongolian Baiti");
      break;
    case Script::TIBETAN:
      aFontList.AppendElement("Microsoft Himalaya");
      break;
    case Script::PHAGS_PA:
      aFontList.AppendElement("Microsoft PhagsPa");
      break;

    case Script::ARABIC:
      // Default to Arial (added unconditionally below) for Arabic script.
      break;
    case Script::ARABIC_NASTALIQ:
      aFontList.AppendElement("Urdu Typesetting");
      break;
    case Script::SYRIAC:
    case Script::ESTRANGELO_SYRIAC:
      aFontList.AppendElement("Estrangelo Edessa");
      break;
    case Script::THAANA:
      aFontList.AppendElement("MV Boli");
      break;

    case Script::BENGALI:
      aFontList.AppendElement("Vrinda");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::DEVANAGARI:
      aFontList.AppendElement("Kokila");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::GUJARATI:
      aFontList.AppendElement("Shruti");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::GURMUKHI:
      aFontList.AppendElement("Raavi");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::KANNADA:
      aFontList.AppendElement("Tunga");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::MALAYALAM:
      aFontList.AppendElement("Kartika");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::ORIYA:
      aFontList.AppendElement("Kalinga");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::TAMIL:
      aFontList.AppendElement("Latha");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::TELUGU:
      aFontList.AppendElement("Gautami");
      aFontList.AppendElement("Nirmala UI");
      break;
    case Script::SINHALA:
      aFontList.AppendElement("Iskoola Pota");
      aFontList.AppendElement("Nirmala UI");
      break;

    case Script::CHAKMA:
    case Script::MEETEI_MAYEK:
    case Script::OL_CHIKI:
    case Script::SORA_SOMPENG:
      aFontList.AppendElement("Nirmala UI");
      break;

    case Script::MYANMAR:
      aFontList.AppendElement("Myanmar Text");
      break;
    case Script::KHMER:
      aFontList.AppendElement("Khmer UI");
      break;
    case Script::LAO:
      aFontList.AppendElement("Lao UI");
      break;
    case Script::THAI:
      aFontList.AppendElement("Tahoma");
      aFontList.AppendElement("Leelawadee UI");
      break;
    case Script::TAI_LE:
      aFontList.AppendElement("Microsoft Tai Le");
      break;
    case Script::BUGINESE:
      aFontList.AppendElement("Leelawadee UI");
      break;
    case Script::NEW_TAI_LUE:
      aFontList.AppendElement("Microsoft New Tai Lue");
      break;
    case Script::JAVANESE:
      aFontList.AppendElement("Javanese Text");
      break;

    case Script::GEORGIAN:
    case Script::KHUTSURI:
    case Script::LISU:
      aFontList.AppendElement("Segoe UI");
      break;

    case Script::ETHIOPIC:
      aFontList.AppendElement("Nyala");
      aFontList.AppendElement("Ebrima");
      break;

    case Script::ADLAM:
    case Script::NKO:
    case Script::OSMANYA:
    case Script::TIFINAGH:
    case Script::VAI:
      aFontList.AppendElement("Ebrima");
      break;

    case Script::CANADIAN_ABORIGINAL:
      aFontList.AppendElement("Euphemia");
      break;

    case Script::CHEROKEE:
    case Script::OSAGE:
      aFontList.AppendElement("Gadugi");
      break;

    case Script::BRAILLE:
    case Script::DESERET:
      aFontList.AppendElement("Segoe UI Symbol");
      break;

    case Script::BRAHMI:
    case Script::CARIAN:
    case Script::CUNEIFORM:
    case Script::CYPRIOT:
    case Script::EGYPTIAN_HIEROGLYPHS:
    case Script::GLAGOLITIC:
    case Script::GOTHIC:
    case Script::IMPERIAL_ARAMAIC:
    case Script::INSCRIPTIONAL_PAHLAVI:
    case Script::INSCRIPTIONAL_PARTHIAN:
    case Script::KHAROSHTHI:
    case Script::LYCIAN:
    case Script::LYDIAN:
    case Script::MEROITIC_CURSIVE:
    case Script::OGHAM:
    case Script::OLD_ITALIC:
    case Script::OLD_PERSIAN:
    case Script::OLD_SOUTH_ARABIAN:
    case Script::OLD_TURKIC:
    case Script::PHOENICIAN:
    case Script::RUNIC:
    case Script::SHAVIAN:
    case Script::UGARITIC:
      aFontList.AppendElement("Segoe UI Historic");
      break;

      // For some scripts where Windows doesn't supply a font by default,
      // there are Noto fonts that users might have installed:
    case Script::AHOM:
      aFontList.AppendElement("Noto Serif Ahom");
      break;
    case Script::AVESTAN:
      aFontList.AppendElement("Noto Sans Avestan");
      break;
    case Script::BALINESE:
      aFontList.AppendElement("Noto Sans Balinese");
      break;
    case Script::BAMUM:
      aFontList.AppendElement("Noto Sans Bamum");
      break;
    case Script::BASSA_VAH:
      aFontList.AppendElement("Noto Sans Bassa Vah");
      break;
    case Script::BATAK:
      aFontList.AppendElement("Noto Sans Batak");
      break;
    case Script::BHAIKSUKI:
      aFontList.AppendElement("Noto Sans Bhaiksuki");
      break;
    case Script::BUHID:
      aFontList.AppendElement("Noto Sans Buhid");
      break;
    case Script::CAUCASIAN_ALBANIAN:
      aFontList.AppendElement("Noto Sans Caucasian Albanian");
      break;
    case Script::CHAM:
      aFontList.AppendElement("Noto Sans Cham");
      break;
    case Script::COPTIC:
      aFontList.AppendElement("Noto Sans Coptic");
      break;
    case Script::DUPLOYAN:
      aFontList.AppendElement("Noto Sans Duployan");
      break;
    case Script::ELBASAN:
      aFontList.AppendElement("Noto Sans Elbasan");
      break;
    case Script::GRANTHA:
      aFontList.AppendElement("Noto Sans Grantha");
      break;
    case Script::HANIFI_ROHINGYA:
      aFontList.AppendElement("Noto Sans Hanifi Rohingya");
      break;
    case Script::HANUNOO:
      aFontList.AppendElement("Noto Sans Hanunoo");
      break;
    case Script::HATRAN:
      aFontList.AppendElement("Noto Sans Hatran");
      break;
    case Script::KAITHI:
      aFontList.AppendElement("Noto Sans Kaithi");
      break;
    case Script::KAYAH_LI:
      aFontList.AppendElement("Noto Sans Kayah Li");
      break;
    case Script::KHOJKI:
      aFontList.AppendElement("Noto Sans Khojki");
      break;
    case Script::KHUDAWADI:
      aFontList.AppendElement("Noto Sans Khudawadi");
      break;
    case Script::LEPCHA:
      aFontList.AppendElement("Noto Sans Lepcha");
      break;
    case Script::LIMBU:
      aFontList.AppendElement("Noto Sans Limbu");
      break;
    case Script::LINEAR_A:
      aFontList.AppendElement("Noto Sans Linear A");
      break;
    case Script::LINEAR_B:
      aFontList.AppendElement("Noto Sans Linear B");
      break;
    case Script::MAHAJANI:
      aFontList.AppendElement("Noto Sans Mahajani");
      break;
    case Script::MANDAIC:
      aFontList.AppendElement("Noto Sans Mandaic");
      break;
    case Script::MANICHAEAN:
      aFontList.AppendElement("Noto Sans Manichaean");
      break;
    case Script::MARCHEN:
      aFontList.AppendElement("Noto Sans Marchen");
      break;
    case Script::MENDE_KIKAKUI:
      aFontList.AppendElement("Noto Sans Mende Kikakui");
      break;
    case Script::MEROITIC_HIEROGLYPHS:
      aFontList.AppendElement("Noto Sans Meroitic");
      break;
    case Script::MIAO:
      aFontList.AppendElement("Noto Sans Miao");
      break;
    case Script::MODI:
      aFontList.AppendElement("Noto Sans Modi");
      break;
    case Script::MRO:
      aFontList.AppendElement("Noto Sans Mro");
      break;
    case Script::MULTANI:
      aFontList.AppendElement("Noto Sans Multani");
      break;
    case Script::NABATAEAN:
      aFontList.AppendElement("Noto Sans Nabataean");
      break;
    case Script::NEWA:
      aFontList.AppendElement("Noto Sans Newa");
      break;
    case Script::OLD_HUNGARIAN:
      aFontList.AppendElement("Noto Sans Old Hungarian");
      break;
    case Script::OLD_NORTH_ARABIAN:
      aFontList.AppendElement("Noto Sans Old North Arabian");
      break;
    case Script::OLD_PERMIC:
      aFontList.AppendElement("Noto Sans Old Permic");
      break;
    case Script::PAHAWH_HMONG:
      aFontList.AppendElement("Noto Sans Pahawh Hmong");
      break;
    case Script::PALMYRENE:
      aFontList.AppendElement("Noto Sans Palmyrene");
      break;
    case Script::PAU_CIN_HAU:
      aFontList.AppendElement("Noto Sans Pau Cin Hau");
      break;
    case Script::PSALTER_PAHLAVI:
      aFontList.AppendElement("Noto Sans Psalter Pahlavi");
      break;
    case Script::REJANG:
      aFontList.AppendElement("Noto Sans Rejang");
      break;
    case Script::SAMARITAN:
      aFontList.AppendElement("Noto Sans Samaritan");
      break;
    case Script::SAURASHTRA:
      aFontList.AppendElement("Noto Sans Saurashtra");
      break;
    case Script::SHARADA:
      aFontList.AppendElement("Noto Sans Sharada");
      break;
    case Script::SIDDHAM:
      aFontList.AppendElement("Noto Sans Siddham");
      break;
    case Script::SUNDANESE:
      aFontList.AppendElement("Noto Sans Sundanese");
      break;
    case Script::SYLOTI_NAGRI:
      aFontList.AppendElement("Noto Sans Syloti Nagri");
      break;
    case Script::TAGALOG:
      aFontList.AppendElement("Noto Sans Tagalog");
      break;
    case Script::TAGBANWA:
      aFontList.AppendElement("Noto Sans Tagbanwa");
      break;
    case Script::TAI_THAM:
      aFontList.AppendElement("Noto Sans Tai Tham");
      break;
    case Script::TAI_VIET:
      aFontList.AppendElement("Noto Sans Tai Viet");
      break;
    case Script::TAKRI:
      aFontList.AppendElement("Noto Sans Takri");
      break;
    case Script::TIRHUTA:
      aFontList.AppendElement("Noto Sans Tirhuta");
      break;
    case Script::WANCHO:
      aFontList.AppendElement("Noto Sans Wancho");
      break;
    case Script::WARANG_CITI:
      aFontList.AppendElement("Noto Sans Warang Citi");
      break;

    case Script::AFAKA:
    case Script::ANATOLIAN_HIEROGLYPHS:
    case Script::BLISSYMBOLS:
    case Script::BOOK_PAHLAVI:
    case Script::CHORASMIAN:
    case Script::CIRTH:
    case Script::CYPRO_MINOAN:
    case Script::DEMOTIC_EGYPTIAN:
    case Script::DIVES_AKURU:
    case Script::DOGRA:
    case Script::EASTERN_SYRIAC:
    case Script::ELYMAIC:
    case Script::GARAY:
    case Script::GUNJALA_GONDI:
    case Script::GURUNG_KHEMA:
    case Script::HARAPPAN_INDUS:
    case Script::HIERATIC_EGYPTIAN:
    case Script::JURCHEN:
    case Script::KAWI:
    case Script::KHITAN_SMALL_SCRIPT:
    case Script::KIRAT_RAI:
    case Script::KPELLE:
    case Script::LATIN_FRAKTUR:
    case Script::LATIN_GAELIC:
    case Script::LOMA:
    case Script::MAKASAR:
    case Script::MASARAM_GONDI:
    case Script::MAYAN_HIEROGLYPHS:
    case Script::MEDEFAIDRIN:
    case Script::MOON:
    case Script::NAG_MUNDARI:
    case Script::NAKHI_GEBA:
    case Script::NANDINAGARI:
    case Script::NUSHU:
    case Script::NYIAKENG_PUACHUE_HMONG:
    case Script::OL_ONAL:
    case Script::OLD_CHURCH_SLAVONIC_CYRILLIC:
    case Script::OLD_SOGDIAN:
    case Script::OLD_UYGHUR:
    case Script::RONGORONGO:
    case Script::SARATI:
    case Script::SIGNWRITING:
    case Script::SOGDIAN:
    case Script::SOYOMBO:
    case Script::SUNUWAR:
    case Script::TANGSA:
    case Script::TANGUT:
    case Script::TENGWAR:
    case Script::TODHRI:
    case Script::TOTO:
    case Script::TULU_TIGALARI:
    case Script::UNKNOWN:
    case Script::UNWRITTEN_LANGUAGES:
    case Script::VISIBLE_SPEECH:
    case Script::VITHKUQI:
    case Script::WESTERN_SYRIAC:
    case Script::WOLEAI:
    case Script::YEZIDI:
    case Script::ZANABAZAR_SQUARE:
      break;
  }

  // Arial is used as default fallback for system fallback, so always try that.
  aFontList.AppendElement("Arial");

  // Symbols/dingbats are generally Script=COMMON but may be resolved to any
  // surrounding script run. So we'll always append a couple of likely fonts
  // for such characters.
  const uint32_t b = aCh >> 8;
  if (aRunScript == Script::COMMON ||  // Stray COMMON chars not resolved
      (b >= 0x20 && b <= 0x2b) || b == 0x2e ||  // BMP symbols/punctuation/etc
      GetGenCategory(aCh) == nsUGenCategory::kSymbol ||
      GetGenCategory(aCh) == nsUGenCategory::kPunctuation) {
    aFontList.AppendElement("Segoe UI Symbol");
    aFontList.AppendElement("Cambria Math");
  }

  // Arial Unicode MS has lots of glyphs for obscure characters; try it as a
  // last resort.
  aFontList.AppendElement("Arial Unicode MS");

  // If we didn't begin with the color-emoji fonts, include them here
  // so that they'll be preferred over user-installed (and possibly
  // broken) fonts in the global fallback path.
  if (!PrefersColor(aPresentation)) {
    aFontList.AppendElement("Segoe UI Emoji");
    aFontList.AppendElement("Twemoji Mozilla");
  }
}

bool gfxWindowsPlatform::DidRenderingDeviceReset(
    mozilla::gfx::DeviceResetReason* aResetReason) {
  DeviceManagerDx* dm = DeviceManagerDx::Get();
  if (!dm) {
    return false;
  }
  return dm->HasDeviceReset(aResetReason);
}

void gfxWindowsPlatform::CompositorUpdated() {
  DeviceManagerDx::Get()->ForceDeviceReset(
      mozilla::gfx::ForcedDeviceResetReason::COMPOSITOR_UPDATED);
  UpdateRenderMode();
}

BOOL CALLBACK InvalidateWindowForDeviceReset(HWND aWnd, LPARAM aMsg) {
  RedrawWindow(aWnd, nullptr, nullptr,
               RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_FRAME);
  return TRUE;
}

void gfxWindowsPlatform::SchedulePaintIfDeviceReset() {
  AUTO_PROFILER_LABEL("gfxWindowsPlatform::SchedulePaintIfDeviceReset", OTHER);

  mozilla::gfx::DeviceResetReason resetReason =
      mozilla::gfx::DeviceResetReason::OK;
  if (!DidRenderingDeviceReset(&resetReason)) {
    return;
  }

  gfxCriticalNote << "(gfxWindowsPlatform) Detected device reset: "
                  << (int)resetReason;

  if (XRE_IsParentProcess()) {
    // Trigger an ::OnPaint for each window.
    ::EnumThreadWindows(GetCurrentThreadId(), InvalidateWindowForDeviceReset,
                        0);
  } else {
    NS_DispatchToMainThread(NS_NewRunnableFunction(
        "gfx::gfxWindowsPlatform::SchedulePaintIfDeviceReset", []() -> void {
          gfxWindowsPlatform::GetPlatform()->CheckForContentOnlyDeviceReset();
        }));
  }

  gfxCriticalNote << "(gfxWindowsPlatform) scheduled device update.";
}

void gfxWindowsPlatform::CheckForContentOnlyDeviceReset() {
  if (!DidRenderingDeviceReset()) {
    return;
  }

  bool isContentOnlyTDR;
  D3D11DeviceStatus status;

  DeviceManagerDx::Get()->ExportDeviceInfo(&status);
  CompositorBridgeChild::Get()->SendCheckContentOnlyTDR(status.sequenceNumber(),
                                                        &isContentOnlyTDR);

  // The parent process doesn't know about the reset yet, or the reset is
  // local to our device.
  if (isContentOnlyTDR) {
    gfxCriticalNote << "A content-only TDR is detected.";
    dom::ContentChild* cc = dom::ContentChild::GetSingleton();
    cc->RecvReinitRenderingForDeviceReset();
  }
}

nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData() {
  if (XRE_IsContentProcess()) {
    auto& cmsOutputProfileData = GetCMSOutputProfileData();
    // We should have set our profile data when we received our initial
    // ContentDeviceData.
    MOZ_ASSERT(cmsOutputProfileData.isSome(),
               "Should have created output profile data when we received "
               "initial content device data.");

    // If we have data, it should not be empty.
    MOZ_ASSERT_IF(cmsOutputProfileData.isSome(),
                  !cmsOutputProfileData->IsEmpty());

    if (cmsOutputProfileData.isSome()) {
      return cmsOutputProfileData.ref().Clone();
    }
    return nsTArray<uint8_t>();
  }

  return GetPlatformCMSOutputProfileData_Impl();
}

nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl() {
  static nsTArray<uint8_t> sCached = [&] {
    // Check override pref first:
    nsTArray<uint8_t> prefProfileData =
        gfxPlatform::GetPrefCMSOutputProfileData();
    if (!prefProfileData.IsEmpty()) {
      return prefProfileData;
    }

    // -
    // Otherwise, create a dummy DC and pull from that.

    HDC dc = ::GetDC(nullptr);
    if (!dc) {
      return nsTArray<uint8_t>();
    }

    WCHAR profilePath[MAX_PATH];
    DWORD profilePathLen = MAX_PATH;

    bool getProfileResult = ::GetICMProfileW(dc, &profilePathLen, profilePath);

    ::ReleaseDC(nullptr, dc);

    if (!getProfileResult) {
      return nsTArray<uint8_t>();
    }

    void* mem = nullptr;
    size_t size = 0;

    qcms_data_from_unicode_path(profilePath, &mem, &size);
    if (!mem) {
      return nsTArray<uint8_t>();
    }

    nsTArray<uint8_t> result;
    result.AppendElements(static_cast<uint8_t*>(mem), size);

    free(mem);

    return result;
  }();

  return sCached.Clone();
}

void gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath,
                                       nsAString& aVersion) {
  DWORD versInfoSize, vers[4] = {0};
  // version info not available case
  aVersion.AssignLiteral(u"0.0.0.0");
  versInfoSize = GetFileVersionInfoSizeW(aDLLPath, nullptr);
  AutoTArray<BYTE, 512> versionInfo;

  if (versInfoSize == 0) {
    return;
  }

  // XXX(Bug 1631371) Check if this should use a fallible operation as it
  // pretended earlier.
  versionInfo.AppendElements(uint32_t(versInfoSize));

  if (!GetFileVersionInfoW(aDLLPath, 0, versInfoSize,
                           LPBYTE(versionInfo.Elements()))) {
    return;
  }

  UINT len = 0;
  VS_FIXEDFILEINFO* fileInfo = nullptr;
  if (!VerQueryValue(LPBYTE(versionInfo.Elements()), TEXT("\\"),
                     (LPVOID*)&fileInfo, &len) ||
      len == 0 || fileInfo == nullptr) {
    return;
  }

  DWORD fileVersMS = fileInfo->dwFileVersionMS;
  DWORD fileVersLS = fileInfo->dwFileVersionLS;

  vers[0] = HIWORD(fileVersMS);
  vers[1] = LOWORD(fileVersMS);
  vers[2] = HIWORD(fileVersLS);
  vers[3] = LOWORD(fileVersLS);

  char buf[256];
  SprintfLiteral(buf, "%lu.%lu.%lu.%lu", vers[0], vers[1], vers[2], vers[3]);
  aVersion.Assign(NS_ConvertUTF8toUTF16(buf));
}

static BOOL CALLBACK AppendClearTypeParams(HMONITOR aMonitor, HDC, LPRECT,
                                           LPARAM aContext) {
  MONITORINFOEXW monitorInfo;
  monitorInfo.cbSize = sizeof(MONITORINFOEXW);
  if (!GetMonitorInfoW(aMonitor, &monitorInfo)) {
    return TRUE;
  }

  ClearTypeParameterInfo ctinfo;
  ctinfo.displayName.Assign(monitorInfo.szDevice);

  RefPtr<IDWriteRenderingParams> renderingParams;
  HRESULT hr = Factory::GetDWriteFactory()->CreateMonitorRenderingParams(
      aMonitor, getter_AddRefs(renderingParams));
  if (FAILED(hr)) {
    return TRUE;
  }

  ctinfo.gamma = renderingParams->GetGamma() * 1000;
  ctinfo.pixelStructure = renderingParams->GetPixelGeometry();
  ctinfo.clearTypeLevel = renderingParams->GetClearTypeLevel() * 100;
  ctinfo.enhancedContrast = renderingParams->GetEnhancedContrast() * 100;

  auto* params = reinterpret_cast<nsTArray<ClearTypeParameterInfo>*>(aContext);
  params->AppendElement(ctinfo);
  return TRUE;
}

void gfxWindowsPlatform::GetCleartypeParams(
    nsTArray<ClearTypeParameterInfo>& aParams) {
  aParams.Clear();
  if (!DWriteEnabled()) {
    return;
  }
  EnumDisplayMonitors(nullptr, nullptr, AppendClearTypeParams,
                      reinterpret_cast<LPARAM>(&aParams));
}

void gfxWindowsPlatform::FontsPrefsChanged(const char* aPref) {
  bool clearTextFontCaches = true;

  gfxPlatform::FontsPrefsChanged(aPref);

  if (aPref &&
      !strncmp(GFX_CLEARTYPE_PARAMS, aPref, strlen(GFX_CLEARTYPE_PARAMS))) {
    gfxDWriteFont::UpdateClearTypeVars();
  } else {
    clearTextFontCaches = false;
  }

  if (clearTextFontCaches) {
    gfxFontCache* fc = gfxFontCache::GetCache();
    if (fc) {
      fc->Flush();
    }
  }
}

bool gfxWindowsPlatform::IsOptimus() {
  static int knowIsOptimus = -1;
  if (knowIsOptimus == -1) {
    // other potential optimus -- nvd3d9wrapx.dll & nvdxgiwrap.dll
    if (GetModuleHandleA("nvumdshim.dll") ||
        GetModuleHandleA("nvumdshimx.dll")) {
      knowIsOptimus = 1;
    } else {
      knowIsOptimus = 0;
    }
  }
  return knowIsOptimus;
}

static void InitializeANGLEConfig() {
  FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);

  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    d3d11ANGLE.DisableByDefault(FeatureStatus::Unavailable,
                                "D3D11 compositing is disabled",
                                "FEATURE_FAILURE_HW_ANGLE_D3D11_DISABLED"_ns);
    return;
  }

  d3d11ANGLE.EnableByDefault();

  nsCString message;
  nsCString failureId;
  if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
                                        &message, failureId)) {
    d3d11ANGLE.Disable(FeatureStatus::Blocklisted, message.get(), failureId);
  }
}

void gfxWindowsPlatform::InitializeDirectDrawConfig() {
  MOZ_ASSERT(XRE_IsParentProcess());

  FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
  ddraw.EnableByDefault();
}

void gfxWindowsPlatform::InitializeConfig() {
  if (XRE_IsParentProcess()) {
    // The parent process first determines which features can be attempted.
    // This information is relayed to content processes and the GPU process.
    InitializeD3D11Config();
    InitializeANGLEConfig();
    InitializeD2DConfig();
  } else {
    ImportCachedContentDeviceData();
    InitializeANGLEConfig();
  }
}

void gfxWindowsPlatform::InitializeD3D11Config() {
  MOZ_ASSERT(XRE_IsParentProcess());

  FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);

  if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
    d3d11.DisableByDefault(FeatureStatus::Unavailable,
                           "Hardware compositing is disabled",
                           "FEATURE_FAILURE_D3D11_NEED_HWCOMP"_ns);
    return;
  }

  d3d11.EnableByDefault();

  // Check if the user really, really wants WARP.
  if (StaticPrefs::layers_d3d11_force_warp_AtStartup()) {
    // Force D3D11 on even if we disabled it.
    d3d11.UserForceEnable("User force-enabled WARP");
  }

  nsCString message;
  nsCString failureId;
  if (StaticPrefs::layers_d3d11_enable_blacklist_AtStartup() &&
      !gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
                                        &message, failureId)) {
    d3d11.Disable(FeatureStatus::Blocklisted, message.get(), failureId);
  }
}

/* static */
void gfxWindowsPlatform::RecordContentDeviceFailure(
    TelemetryDeviceCode aDevice) {
  // If the parent process fails to acquire a device, we record this
  // normally as part of the environment. The exceptional case we're
  // looking for here is when the parent process successfully acquires
  // a device, but the content process fails to acquire the same device.
  // This would not normally be displayed in about:support.
  if (!XRE_IsContentProcess()) {
    return;
  }
  Telemetry::Accumulate(Telemetry::GFX_CONTENT_FAILED_TO_ACQUIRE_DEVICE,
                        uint32_t(aDevice));
}

void gfxWindowsPlatform::RecordStartupTelemetry() {
  if (!XRE_IsParentProcess()) {
    return;
  }

  DeviceManagerDx* dx = DeviceManagerDx::Get();
  nsTArray<DXGI_OUTPUT_DESC1> outputs = dx->EnumerateOutputs();

  uint32_t allSupportedColorSpaces = 0;
  for (auto& output : outputs) {
    uint32_t colorSpace = 1 << output.ColorSpace;
    allSupportedColorSpaces |= colorSpace;
  }

  glean::gfx_hdr::windows_display_colorspace_bitfield.Set(
      allSupportedColorSpaces);
}

// Supports lazy device initialization on Windows, so that WebRender can avoid
// initializing GPU state and allocating swap chains for most non-GPU processes.
void gfxWindowsPlatform::EnsureDevicesInitialized() {
  MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());

  if (!mInitializedDevices) {
    mInitializedDevices = true;
    InitializeDevices();
    UpdateBackendPrefs();
  }
}

bool gfxWindowsPlatform::DevicesInitialized() { return mInitializedDevices; }

void gfxWindowsPlatform::InitializeDevices() {
  MOZ_ASSERT(NS_IsMainThread());

  if (XRE_IsParentProcess()) {
    // If we're the UI process, and the GPU process is enabled, then we don't
    // initialize any DirectX devices. We do leave them enabled in gfxConfig
    // though. If the GPU process fails to create these devices it will send
    // a message back and we'll update their status.
    if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
      return;
    }

    // No GPU process, continue initializing devices as normal.
  }

  // If acceleration is disabled, we refuse to initialize anything.
  if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
    return;
  }

  // If we previously crashed initializing devices, bail out now.
  D3D11LayersCrashGuard detectCrashes;
  if (detectCrashes.Crashed()) {
    gfxConfig::SetFailed(Feature::HW_COMPOSITING,
                         FeatureStatus::CrashedOnStartup,
                         "Crashed during startup in a previous session");
    gfxConfig::SetFailed(
        Feature::D3D11_COMPOSITING, FeatureStatus::CrashedOnStartup,
        "Harware acceleration crashed during startup in a previous session");
    gfxConfig::SetFailed(
        Feature::DIRECT2D, FeatureStatus::CrashedOnStartup,
        "Harware acceleration crashed during startup in a previous session");
    return;
  }

  bool shouldUseD2D = gfxConfig::IsEnabled(Feature::DIRECT2D);

  // First, initialize D3D11. If this succeeds we attempt to use Direct2D.
  InitializeD3D11();
  InitializeD2D();

  if (!gfxConfig::IsEnabled(Feature::DIRECT2D) && XRE_IsContentProcess() &&
      shouldUseD2D) {
    RecordContentDeviceFailure(TelemetryDeviceCode::D2D1);
  }
}

void gfxWindowsPlatform::InitializeD3D11() {
  // This function attempts to initialize our D3D11 devices, if the hardware
  // is not blocklisted for D3D11 layers. This first attempt will try to create
  // a hardware accelerated device. If this creation fails or the hardware is
  // blocklisted, then this function will abort if WARP is disabled, causing us
  // to fallback to Basic layers. If WARP is not disabled it will use a WARP
  // device which should always be available on Windows 7 and higher.
  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    return;
  }

  DeviceManagerDx* dm = DeviceManagerDx::Get();
  if (XRE_IsParentProcess()) {
    if (!dm->CreateCompositorDevices()) {
      return;
    }
  }

  dm->CreateContentDevices();

  // Content process failed to create the d3d11 device while parent process
  // succeed.
  if (XRE_IsContentProcess() &&
      !gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    gfxCriticalError()
        << "[D3D11] Failed to create the D3D11 device in content \
                           process.";
  }
}

void gfxWindowsPlatform::InitializeD2DConfig() {
  FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);

  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    d2d1.DisableByDefault(FeatureStatus::Unavailable,
                          "Direct2D requires Direct3D 11 compositing",
                          "FEATURE_FAILURE_D2D_D3D11_COMP"_ns);
    return;
  }

  d2d1.SetDefaultFromPref(StaticPrefs::GetPrefName_gfx_direct2d_disabled(),
                          false,
                          StaticPrefs::GetPrefDefault_gfx_direct2d_disabled());

  nsCString message;
  nsCString failureId;
  if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT2D, &message,
                                        failureId)) {
    d2d1.Disable(FeatureStatus::Blocklisted, message.get(), failureId);
  }

  if (!d2d1.IsEnabled() &&
      StaticPrefs::gfx_direct2d_force_enabled_AtStartup()) {
    d2d1.UserForceEnable("Force-enabled via user-preference");
  }
}

void gfxWindowsPlatform::InitializeD2D() {
  ScopedGfxFeatureReporter d2d1_1("D2D1.1");

  FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);

  DeviceManagerDx* dm = DeviceManagerDx::Get();

  // We don't know this value ahead of time, but the user can force-override
  // it, so we use Disable instead of SetFailed.
  if (dm->IsWARP()) {
    d2d1.Disable(FeatureStatus::Blocked,
                 "Direct2D is not compatible with Direct3D11 WARP",
                 "FEATURE_FAILURE_D2D_WARP_BLOCK"_ns);
  }

  // If we pass all the initial checks, we can proceed to runtime decisions.
  if (!d2d1.IsEnabled()) {
    return;
  }

  if (!Factory::SupportsD2D1()) {
    d2d1.SetFailed(FeatureStatus::Unavailable,
                   "Failed to acquire a Direct2D 1.1 factory",
                   "FEATURE_FAILURE_D2D_FACTORY"_ns);
    return;
  }

  if (!dm->GetContentDevice()) {
    d2d1.SetFailed(FeatureStatus::Failed,
                   "Failed to acquire a Direct3D 11 content device",
                   "FEATURE_FAILURE_D2D_DEVICE"_ns);
    return;
  }

  if (!dm->TextureSharingWorks()) {
    d2d1.SetFailed(FeatureStatus::Failed,
                   "Direct3D11 device does not support texture sharing",
                   "FEATURE_FAILURE_D2D_TXT_SHARING"_ns);
    return;
  }

  // Using Direct2D depends on DWrite support.
  if (!DWriteEnabled() && !InitDWriteSupport()) {
    d2d1.SetFailed(FeatureStatus::Failed,
                   "Failed to initialize DirectWrite support",
                   "FEATURE_FAILURE_D2D_DWRITE"_ns);
    return;
  }

  // Verify that Direct2D device creation succeeded.
  RefPtr<ID3D11Device> contentDevice = dm->GetContentDevice();
  if (!Factory::SetDirect3D11Device(contentDevice)) {
    d2d1.SetFailed(FeatureStatus::Failed, "Failed to create a Direct2D device",
                   "FEATURE_FAILURE_D2D_CREATE_FAILED"_ns);
    return;
  }

  MOZ_ASSERT(d2d1.IsEnabled());
  d2d1_1.SetSuccessful();
}

void gfxWindowsPlatform::InitGPUProcessSupport() {
  FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);

  if (!gpuProc.IsEnabled()) {
    return;
  }

  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    // Don't use the GPU process if not using D3D11, unless software
    // compositor is allowed
    if (StaticPrefs::layers_gpu_process_allow_software_AtStartup()) {
      return;
    }
    gpuProc.Disable(FeatureStatus::Unavailable,
                    "Not using GPU Process since D3D11 is unavailable",
                    "FEATURE_FAILURE_NO_D3D11"_ns);
  }
  // If we're still enabled at this point, the user set the force-enabled pref.
}

class D3DVsyncSource final : public VsyncSource {
 public:
  D3DVsyncSource()
      : mPrevVsync(TimeStamp::Now()),
        mVsyncEnabled(false),
        mWaitVBlankMonitor(NULL) {
    mVsyncThread = new base::Thread("WindowsVsyncThread");
    MOZ_RELEASE_ASSERT(mVsyncThread->Start(),
                       "GFX: Could not start Windows vsync thread");
    SetVsyncRate();
  }

  void SetVsyncRate() {
    DWM_TIMING_INFO vblankTime;
    // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
    vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
    HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
    if (SUCCEEDED(hr)) {
      UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
      // We get the rate in hertz / time, but we want the rate in ms.
      float rate =
          ((float)refreshRate.uiDenominator / (float)refreshRate.uiNumerator) *
          1000;
      mVsyncRate = TimeDuration::FromMilliseconds(rate);
    } else {
      mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
    }
  }

  virtual void Shutdown() override {
    MOZ_ASSERT(NS_IsMainThread());
    DisableVsync();
    mVsyncThread->Stop();
    delete mVsyncThread;
  }

  virtual void EnableVsync() override {
    MOZ_ASSERT(NS_IsMainThread());
    MOZ_ASSERT(mVsyncThread->IsRunning());
    {  // scope lock
      if (mVsyncEnabled) {
        return;
      }
      mVsyncEnabled = true;
    }

    mVsyncThread->message_loop()->PostTask(NewRunnableMethod(
        "D3DVsyncSource::VBlankLoop"this, &D3DVsyncSource::VBlankLoop));
  }

  virtual void DisableVsync() override {
    MOZ_ASSERT(NS_IsMainThread());
    MOZ_ASSERT(mVsyncThread->IsRunning());
    if (!mVsyncEnabled) {
      return;
    }
    mVsyncEnabled = false;
  }

  virtual bool IsVsyncEnabled() override {
    MOZ_ASSERT(NS_IsMainThread());
    return mVsyncEnabled;
  }

  virtual TimeDuration GetVsyncRate() override { return mVsyncRate; }

  void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp) {
    MOZ_ASSERT(IsInVsyncThread());
    NS_WARNING(
        "DwmComposition dynamically disabled, falling back to software "
        "timers");

    TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
    TimeDuration delay = nextVsync - TimeStamp::Now();
    if (delay.ToMilliseconds() < 0) {
      delay = mozilla::TimeDuration::FromMilliseconds(0);
    }

    mVsyncThread->message_loop()->PostDelayedTask(
        NewRunnableMethod("D3DVsyncSource::VBlankLoop"this,
                          &D3DVsyncSource::VBlankLoop),
        delay.ToMilliseconds());
  }

  // Returns the timestamp for the just happened vsync
  TimeStamp GetVBlankTime() {
    TimeStamp vsync = TimeStamp::Now();
    TimeStamp now = vsync;

    DWM_TIMING_INFO vblankTime;
    // Make sure to init the cbSize, otherwise
    // GetCompositionTiming will fail
    vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
    HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
    if (!SUCCEEDED(hr)) {
      return vsync;
    }

    LARGE_INTEGER frequency;
    QueryPerformanceFrequency(&frequency);

    LARGE_INTEGER qpcNow;
    QueryPerformanceCounter(&qpcNow);

    const int microseconds = 1000000;
    int64_t adjust = qpcNow.QuadPart - vblankTime.qpcVBlank;
    int64_t usAdjust = (adjust * microseconds) / frequency.QuadPart;
    vsync -= TimeDuration::FromMicroseconds((double)usAdjust);

    // On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
    // reports the upcoming vsync time, which is in the future.
    // It can also sometimes report a vblank time in the past.
    // Since large parts of Gecko assume TimeStamps can't be in future,
    // use the previous vsync.

    // Windows 10 and Intel HD vsync timestamps are messy and
    // all over the place once in a while. Most of the time,
    // it reports the upcoming vsync. Sometimes, that upcoming
    // vsync is in the past. Sometimes that upcoming vsync is before
    // the previously seen vsync.
    // In these error cases, normalize to Now();
    if (vsync >= now) {
      vsync = vsync - mVsyncRate;
    }

    // On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
    // from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
    if (vsync >= now) {
      vsync = now;
    }

    // Our vsync time is some time very far in the past, adjust to Now.
    // 4 ms is arbitrary, so feel free to pick something else if this isn't
    // working. See the comment above.
    if ((now - vsync).ToMilliseconds() > 4.0) {
      vsync = now;
    }

    return vsync;
  }

  void VBlankLoop() {
    MOZ_ASSERT(IsInVsyncThread());
    MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));

    TimeStamp vsync = TimeStamp::Now();
    mPrevVsync = TimeStamp();
    TimeStamp flushTime = TimeStamp::Now();
    TimeDuration longVBlank = mVsyncRate * 2;

    for (;;) {
      {  // scope lock
        if (!mVsyncEnabled) return;
      }

      // Large parts of gecko assume that the refresh driver timestamp
      // must be <= Now() and cannot be in the future.
      MOZ_ASSERT(vsync <= TimeStamp::Now());
      NotifyVsync(vsync, vsync + mVsyncRate);

      HRESULT hr = E_FAIL;
      if (!StaticPrefs::gfx_vsync_force_disable_waitforvblank()) {
        UpdateVBlankOutput();
        if (mWaitVBlankOutput) {
          const TimeStamp vblank_begin_wait = TimeStamp::Now();
          {
            AUTO_PROFILER_THREAD_SLEEP;
            hr = mWaitVBlankOutput->WaitForVBlank();
          }
          if (SUCCEEDED(hr)) {
            // vblank might return instantly when running headless,
            // monitor powering off, etc.  Since we're on a dedicated
            // thread, instant-return should not happen in the normal
            // case, so catch any odd behavior with a time cutoff:
            TimeDuration vblank_wait = TimeStamp::Now() - vblank_begin_wait;
            if (vblank_wait.ToMilliseconds() < 1.0) {
              hr = E_FAIL;  // fall back on old behavior
            }
          }
        }
      }
      if (!SUCCEEDED(hr)) {
        hr = DwmFlush();
      }
      if (!SUCCEEDED(hr)) {
        // DWMFlush isn't working, fallback to software vsync.
        ScheduleSoftwareVsync(TimeStamp::Now());
        return;
      }

      TimeStamp now = TimeStamp::Now();
      TimeDuration flushDiff = now - flushTime;
      flushTime = now;
      if ((flushDiff > longVBlank) || mPrevVsync.IsNull()) {
        // Our vblank took longer than 2 intervals, readjust our timestamps
        vsync = GetVBlankTime();
        mPrevVsync = vsync;
      } else {
        // Instead of giving the actual vsync time, a constant interval
        // between vblanks instead of the noise generated via hardware
        // is actually what we want. Most apps just care about the diff
        // between vblanks to animate, so a clean constant interval is
        // smoother.
        vsync = mPrevVsync + mVsyncRate;
        if (vsync > now) {
          // DWMFlush woke up very early, so readjust our times again
          vsync = GetVBlankTime();
        }

        if (vsync <= mPrevVsync) {
          vsync = TimeStamp::Now();
        }

        if ((now - vsync).ToMilliseconds() > 2.0) {
          // Account for time drift here where vsync never quite catches up to
          // Now and we'd fall ever so slightly further behind Now().
          vsync = GetVBlankTime();
        }

        mPrevVsync = vsync;
      }
    }  // end for
  }
  virtual ~D3DVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }

 private:
  bool IsInVsyncThread() {
    return mVsyncThread->thread_id() == PlatformThread::CurrentId();
  }

  void UpdateVBlankOutput() {
    HMONITOR primary_monitor =
        MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
    if (primary_monitor == mWaitVBlankMonitor && mWaitVBlankOutput) {
      return;
    }

    mWaitVBlankMonitor = primary_monitor;

    RefPtr<IDXGIOutput> output = nullptr;
    if (DeviceManagerDx* dx = DeviceManagerDx::Get()) {
      if (dx->GetOutputFromMonitor(mWaitVBlankMonitor, &output)) {
        mWaitVBlankOutput = output;
        return;
      }
    }

    // failed to convert a monitor to an output so keep trying
    mWaitVBlankOutput = nullptr;
  }

  TimeStamp mPrevVsync;
  base::Thread* mVsyncThread;
  TimeDuration mVsyncRate;
  Atomic<bool> mVsyncEnabled;

  HMONITOR mWaitVBlankMonitor;
  RefPtr<IDXGIOutput> mWaitVBlankOutput;
};  // D3DVsyncSource

already_AddRefed<mozilla::gfx::VsyncSource>
gfxWindowsPlatform::CreateGlobalHardwareVsyncSource() {
  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "GFX: Not in main thread.");

  RefPtr<VsyncSource> d3dVsyncSource = new D3DVsyncSource();
  return d3dVsyncSource.forget();
}

void gfxWindowsPlatform::ImportGPUDeviceData(
    const mozilla::gfx::GPUDeviceData& aData) {
  MOZ_ASSERT(XRE_IsParentProcess());

  gfxPlatform::ImportGPUDeviceData(aData);

  gfxConfig::ImportChange(Feature::D3D11_COMPOSITING, aData.d3d11Compositing());

  DeviceManagerDx* dm = DeviceManagerDx::Get();
  if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    dm->ImportDeviceInfo(aData.gpuDevice().ref());
  } else {
    // There should be no devices, so this just takes away the device status.
    dm->ResetDevices();

    // Make sure we disable D2D if content processes might use it.
    FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
    if (d2d1.IsEnabled()) {
      d2d1.SetFailed(FeatureStatus::Unavailable,
                     "Direct2D requires Direct3D 11 compositing",
                     "FEATURE_FAILURE_D2D_D3D11_COMP"_ns);
    }
  }

  // CanUseHardwareVideoDecoding depends on d3d11 state, so update
  // the cached value now.
  UpdateCanUseHardwareVideoDecoding();

  // For completeness (and messaging in about:support). Content recomputes this
  // on its own, and we won't use ANGLE in the UI process if we're using a GPU
  // process.
  UpdateANGLEConfig();
}

void gfxWindowsPlatform::ImportContentDeviceData(
    const mozilla::gfx::ContentDeviceData& aData) {
  MOZ_ASSERT(XRE_IsContentProcess());

  gfxPlatform::ImportContentDeviceData(aData);

  const DevicePrefs& prefs = aData.prefs();
  gfxConfig::Inherit(Feature::D3D11_COMPOSITING, prefs.d3d11Compositing());
  gfxConfig::Inherit(Feature::DIRECT2D, prefs.useD2D1());

  if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
    DeviceManagerDx* dm = DeviceManagerDx::Get();
    dm->ImportDeviceInfo(aData.d3d11());
  }
}

void gfxWindowsPlatform::BuildContentDeviceData(ContentDeviceData* aOut) {
  // Check for device resets before giving back new graphics information.
  UpdateRenderMode();

  gfxPlatform::BuildContentDeviceData(aOut);

  const FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
  aOut->prefs().d3d11Compositing() = d3d11.GetValue();
  aOut->prefs().useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D);

  if (d3d11.IsEnabled()) {
    DeviceManagerDx* dm = DeviceManagerDx::Get();
    dm->ExportDeviceInfo(&aOut->d3d11());
  }

  aOut->cmsOutputProfileData() =
      gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfileData();
}

bool gfxWindowsPlatform::CheckVariationFontSupport() {
  // Variation font support is only available on Fall Creators Update or later.
  return IsWin10FallCreatorsUpdateOrLater();
}

void gfxWindowsPlatform::GetPlatformDisplayInfo(
    mozilla::widget::InfoObject& aObj) {
  HwStretchingSupport stretch;
  DeviceManagerDx::Get()->CheckHardwareStretchingSupport(stretch);

  nsPrintfCString stretchValue(
      "both=%u window-only=%u full-screen-only=%u none=%u error=%u",
      stretch.mBoth, stretch.mWindowOnly, stretch.mFullScreenOnly,
      stretch.mNone, stretch.mError);
  aObj.DefineProperty("HardwareStretching", stretchValue.get());

  ScaledResolutionSet scaled;
  GetScaledResolutions(scaled);
  if (scaled.IsEmpty()) {
    return;
  }

  aObj.DefineProperty("ScaledResolutionCount", scaled.Length());
  for (size_t i = 0; i < scaled.Length(); ++i) {
    auto& s = scaled[i];
    nsPrintfCString name("ScaledResolution%zu", i);
    nsPrintfCString value("source %dx%d, target %dx%d", s.first.width,
                          s.first.height, s.second.width, s.second.height);
    aObj.DefineProperty(name.get(), value.get());
  }
}

99%


¤ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge