Bug 1859625 Part 2: Stop sending the GetGraphicsDeviceInitData message. r=ipc-reviewe...
[gecko.git] / gfx / thebes / gfxWindowsPlatform.cpp
blobfb2155fbc01455362e35d88e54e86d1f66f6391e
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/. */
7 #define INITGUID // set before devguid.h
9 #include "gfxWindowsPlatform.h"
11 #include "cairo.h"
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/layers/CompositorBridgeChild.h"
15 #include "gfxBlur.h"
16 #include "gfxImageSurface.h"
17 #include "gfxWindowsSurface.h"
19 #include "nsUnicharUtils.h"
20 #include "nsUnicodeProperties.h"
22 #include "mozilla/Preferences.h"
23 #include "mozilla/ProfilerLabels.h"
24 #include "mozilla/ProfilerThreadSleep.h"
25 #include "mozilla/Components.h"
26 #include "mozilla/Sprintf.h"
27 #include "mozilla/WindowsVersion.h"
28 #include "nsIGfxInfo.h"
29 #include "nsServiceManagerUtils.h"
30 #include "nsTArray.h"
31 #include "nsThreadUtils.h"
32 #include "mozilla/Telemetry.h"
34 #include "plbase64.h"
35 #include "nsIXULRuntime.h"
36 #include "imgLoader.h"
38 #include "nsIGfxInfo.h"
40 #include "gfxCrashReporterUtils.h"
42 #include "gfxGDIFontList.h"
43 #include "gfxGDIFont.h"
45 #include "mozilla/layers/CanvasChild.h"
46 #include "mozilla/layers/CompositorThread.h"
48 #include "gfxDWriteFontList.h"
49 #include "gfxDWriteFonts.h"
50 #include "gfxDWriteCommon.h"
51 #include <dwrite.h>
53 #include "gfxTextRun.h"
54 #include "gfxUserFontSet.h"
55 #include "nsWindowsHelpers.h"
56 #include "gfx2DGlue.h"
58 #include <string>
60 #include <d3d10_1.h>
62 #include "mozilla/gfx/2D.h"
63 #include "mozilla/gfx/gfxVars.h"
65 #include <dwmapi.h>
66 #include <d3d11.h>
67 #include <d2d1_1.h>
69 #include "nsIMemoryReporter.h"
70 #include <winternl.h>
71 #include "d3dkmtQueryStatistics.h"
73 #include "base/thread.h"
74 #include "mozilla/StaticPrefs_gfx.h"
75 #include "mozilla/StaticPrefs_layers.h"
76 #include "gfxConfig.h"
77 #include "VsyncSource.h"
78 #include "DriverCrashGuard.h"
79 #include "mozilla/dom/ContentChild.h"
80 #include "mozilla/gfx/DeviceManagerDx.h"
81 #include "mozilla/gfx/DisplayConfigWindows.h"
82 #include "mozilla/layers/DeviceAttachmentsD3D11.h"
83 #include "mozilla/WindowsProcessMitigations.h"
84 #include "D3D11Checks.h"
86 using namespace mozilla;
87 using namespace mozilla::gfx;
88 using namespace mozilla::layers;
89 using namespace mozilla::widget;
90 using namespace mozilla::image;
91 using namespace mozilla::unicode;
93 DCForMetrics::DCForMetrics() {
94 // Get the whole screen DC:
95 mDC = GetDC(nullptr);
96 SetGraphicsMode(mDC, GM_ADVANCED);
99 class GfxD2DVramReporter final : public nsIMemoryReporter {
100 ~GfxD2DVramReporter() {}
102 public:
103 NS_DECL_ISUPPORTS
105 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
106 nsISupports* aData, bool aAnonymize) override {
107 MOZ_COLLECT_REPORT("gfx-d2d-vram-draw-target", KIND_OTHER, UNITS_BYTES,
108 Factory::GetD2DVRAMUsageDrawTarget(),
109 "Video memory used by D2D DrawTargets.");
111 MOZ_COLLECT_REPORT("gfx-d2d-vram-source-surface", KIND_OTHER, UNITS_BYTES,
112 Factory::GetD2DVRAMUsageSourceSurface(),
113 "Video memory used by D2D SourceSurfaces.");
115 return NS_OK;
119 NS_IMPL_ISUPPORTS(GfxD2DVramReporter, nsIMemoryReporter)
121 class GPUAdapterReporter final : public nsIMemoryReporter {
122 // Callers must Release the DXGIAdapter after use or risk mem-leak
123 static bool GetDXGIAdapter(IDXGIAdapter** aDXGIAdapter) {
124 RefPtr<ID3D11Device> d3d11Device;
125 RefPtr<IDXGIDevice> dxgiDevice;
126 bool result = false;
128 if ((d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) {
129 if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice),
130 getter_AddRefs(dxgiDevice)) == S_OK) {
131 result = (dxgiDevice->GetAdapter(aDXGIAdapter) == S_OK);
135 return result;
138 ~GPUAdapterReporter() {}
140 public:
141 NS_DECL_ISUPPORTS
143 NS_IMETHOD
144 CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
145 bool aAnonymize) override {
146 HANDLE ProcessHandle = GetCurrentProcess();
148 int64_t dedicatedBytesUsed = 0;
149 int64_t sharedBytesUsed = 0;
150 int64_t committedBytesUsed = 0;
151 IDXGIAdapter* DXGIAdapter;
153 HMODULE gdi32Handle;
154 PFND3DKMTQS queryD3DKMTStatistics = nullptr;
156 if ((gdi32Handle = LoadLibrary(TEXT("gdi32.dll"))))
157 queryD3DKMTStatistics =
158 (PFND3DKMTQS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");
160 if (queryD3DKMTStatistics && GetDXGIAdapter(&DXGIAdapter)) {
161 // Most of this block is understood thanks to wj32's work on Process
162 // Hacker
164 DXGI_ADAPTER_DESC adapterDesc;
165 D3DKMTQS queryStatistics;
167 DXGIAdapter->GetDesc(&adapterDesc);
168 DXGIAdapter->Release();
170 memset(&queryStatistics, 0, sizeof(D3DKMTQS));
171 queryStatistics.Type = D3DKMTQS_PROCESS;
172 queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
173 queryStatistics.hProcess = ProcessHandle;
174 if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
175 committedBytesUsed =
176 queryStatistics.QueryResult.ProcessInfo.SystemMemory.BytesAllocated;
179 memset(&queryStatistics, 0, sizeof(D3DKMTQS));
180 queryStatistics.Type = D3DKMTQS_ADAPTER;
181 queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
182 if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
183 ULONG i;
184 ULONG segmentCount = queryStatistics.QueryResult.AdapterInfo.NbSegments;
186 for (i = 0; i < segmentCount; i++) {
187 memset(&queryStatistics, 0, sizeof(D3DKMTQS));
188 queryStatistics.Type = D3DKMTQS_SEGMENT;
189 queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
190 queryStatistics.QuerySegment.SegmentId = i;
192 if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
193 bool aperture = queryStatistics.QueryResult.SegmentInfo.Aperture;
194 memset(&queryStatistics, 0, sizeof(D3DKMTQS));
195 queryStatistics.Type = D3DKMTQS_PROCESS_SEGMENT;
196 queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
197 queryStatistics.hProcess = ProcessHandle;
198 queryStatistics.QueryProcessSegment.SegmentId = i;
199 if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
200 ULONGLONG bytesCommitted =
201 queryStatistics.QueryResult.ProcessSegmentInfo.BytesCommitted;
202 if (aperture)
203 sharedBytesUsed += bytesCommitted;
204 else
205 dedicatedBytesUsed += bytesCommitted;
212 FreeLibrary(gdi32Handle);
214 MOZ_COLLECT_REPORT("gpu-committed", KIND_OTHER, UNITS_BYTES,
215 committedBytesUsed,
216 "Memory committed by the Windows graphics system.");
218 MOZ_COLLECT_REPORT(
219 "gpu-dedicated", KIND_OTHER, UNITS_BYTES, dedicatedBytesUsed,
220 "Out-of-process memory allocated for this process in a physical "
221 "GPU adapter's memory.");
223 MOZ_COLLECT_REPORT("gpu-shared", KIND_OTHER, UNITS_BYTES, sharedBytesUsed,
224 "In-process memory that is shared with the GPU.");
226 return NS_OK;
230 NS_IMPL_ISUPPORTS(GPUAdapterReporter, nsIMemoryReporter)
232 Atomic<size_t> gfxWindowsPlatform::sD3D11SharedTextures;
233 Atomic<size_t> gfxWindowsPlatform::sD3D9SharedTextures;
235 class D3DSharedTexturesReporter final : public nsIMemoryReporter {
236 ~D3DSharedTexturesReporter() {}
238 public:
239 NS_DECL_ISUPPORTS
241 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
242 nsISupports* aData, bool aAnonymize) override {
243 if (gfxWindowsPlatform::sD3D11SharedTextures > 0) {
244 MOZ_COLLECT_REPORT("d3d11-shared-textures", KIND_OTHER, UNITS_BYTES,
245 gfxWindowsPlatform::sD3D11SharedTextures,
246 "D3D11 shared textures.");
249 if (gfxWindowsPlatform::sD3D9SharedTextures > 0) {
250 MOZ_COLLECT_REPORT("d3d9-shared-textures", KIND_OTHER, UNITS_BYTES,
251 gfxWindowsPlatform::sD3D9SharedTextures,
252 "D3D9 shared textures.");
255 return NS_OK;
259 NS_IMPL_ISUPPORTS(D3DSharedTexturesReporter, nsIMemoryReporter)
261 gfxWindowsPlatform::gfxWindowsPlatform()
262 : mRenderMode(RENDER_GDI), mSupportsHDR(false) {
263 // If win32k is locked down then we can't use COM STA and shouldn't need it.
264 // Also, we won't be using any GPU memory in this process.
265 if (!IsWin32kLockedDown()) {
267 * Initialize COM
269 CoInitialize(nullptr);
271 RegisterStrongMemoryReporter(new GfxD2DVramReporter());
272 RegisterStrongMemoryReporter(new GPUAdapterReporter());
273 RegisterStrongMemoryReporter(new D3DSharedTexturesReporter());
277 gfxWindowsPlatform::~gfxWindowsPlatform() {
278 mozilla::gfx::Factory::D2DCleanup();
280 DeviceManagerDx::Shutdown();
282 // We don't initialize COM when win32k is locked down.
283 if (!IsWin32kLockedDown()) {
285 * Uninitialize COM
287 CoUninitialize();
291 /* static */
292 void gfxWindowsPlatform::InitMemoryReportersForGPUProcess() {
293 MOZ_RELEASE_ASSERT(XRE_IsGPUProcess());
295 RegisterStrongMemoryReporter(new GfxD2DVramReporter());
296 RegisterStrongMemoryReporter(new GPUAdapterReporter());
297 RegisterStrongMemoryReporter(new D3DSharedTexturesReporter());
300 /* static */
301 nsresult gfxWindowsPlatform::GetGpuTimeSinceProcessStartInMs(
302 uint64_t* aResult) {
303 // If win32k is locked down then we should not have any GPU processing and
304 // cannot use these APIs either way.
305 if (IsWin32kLockedDown()) {
306 *aResult = 0;
307 return NS_OK;
310 nsModuleHandle module(LoadLibrary(L"gdi32.dll"));
311 if (!module) {
312 return NS_ERROR_NOT_AVAILABLE;
315 PFND3DKMTQS queryD3DKMTStatistics =
316 (PFND3DKMTQS)GetProcAddress(module, "D3DKMTQueryStatistics");
317 if (!queryD3DKMTStatistics) {
318 return NS_ERROR_NOT_AVAILABLE;
321 gfx::DeviceManagerDx* dm = DeviceManagerDx::Get();
322 if (!dm) {
323 return NS_ERROR_NOT_AVAILABLE;
326 D3D11DeviceStatus status;
327 if (!dm->ExportDeviceInfo(&status)) {
328 // Assume that we used 0ms of GPU time if the device manager
329 // doesn't know the device status.
330 *aResult = 0;
331 return NS_OK;
334 const DxgiAdapterDesc& adapterDesc = status.adapter();
336 D3DKMTQS queryStatistics;
337 memset(&queryStatistics, 0, sizeof(D3DKMTQS));
338 queryStatistics.Type = D3DKMTQS_ADAPTER;
339 queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
340 if (!NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
341 return NS_ERROR_FAILURE;
344 uint64_t result = 0;
345 ULONG nodeCount = queryStatistics.QueryResult.AdapterInfo.NodeCount;
346 for (ULONG i = 0; i < nodeCount; ++i) {
347 memset(&queryStatistics, 0, sizeof(D3DKMTQS));
348 queryStatistics.Type = D3DKMTQS_PROCESS_NODE;
349 queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
350 queryStatistics.hProcess = GetCurrentProcess();
351 queryStatistics.QueryProcessNode.NodeId = i;
352 if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
353 result += queryStatistics.QueryResult.ProcessNodeInformation.RunningTime
354 .QuadPart *
355 100 / PR_NSEC_PER_MSEC;
359 *aResult = result;
360 return NS_OK;
363 static void UpdateANGLEConfig() {
364 if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
365 gfxConfig::Disable(Feature::D3D11_HW_ANGLE, FeatureStatus::Disabled,
366 "D3D11 compositing is disabled",
367 "FEATURE_FAILURE_HW_ANGLE_D3D11_DISABLED"_ns);
371 void gfxWindowsPlatform::InitAcceleration() {
372 gfxPlatform::InitAcceleration();
374 DeviceManagerDx::Init();
376 // Content processes should have received content device data from parent.
377 MOZ_ASSERT_IF(XRE_IsContentProcess(), GetInitContentDeviceData());
379 InitializeConfig();
380 InitGPUProcessSupport();
381 // Ensure devices initialization. SharedSurfaceANGLE and
382 // SharedSurfaceD3D11Interop use them. The devices are lazily initialized
383 // with WebRender to reduce memory usage.
384 // Initialize them now when running non-e10s.
385 if (!BrowserTabsRemoteAutostart()) {
386 EnsureDevicesInitialized();
388 UpdateANGLEConfig();
389 UpdateRenderMode();
391 // If we have Skia and we didn't init dwrite already, do it now.
392 if (!DWriteEnabled() && GetDefaultContentBackend() == BackendType::SKIA) {
393 InitDWriteSupport();
395 // We need to listen for font setting changes even if DWrite is not used.
396 Factory::SetSystemTextQuality(gfxVars::SystemTextQuality());
397 gfxVars::SetSystemTextQualityListener(
398 gfxDWriteFont::SystemTextQualityChanged);
400 // CanUseHardwareVideoDecoding depends on DeviceManagerDx state,
401 // so update the cached value now.
402 UpdateCanUseHardwareVideoDecoding();
403 UpdateSupportsHDR();
405 RecordStartupTelemetry();
408 void gfxWindowsPlatform::InitWebRenderConfig() {
409 gfxPlatform::InitWebRenderConfig();
410 UpdateBackendPrefs();
413 bool gfxWindowsPlatform::CanUseHardwareVideoDecoding() {
414 DeviceManagerDx* dm = DeviceManagerDx::Get();
415 if (!dm) {
416 return false;
418 if (!dm->TextureSharingWorks()) {
419 return false;
421 return !dm->IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
424 bool gfxWindowsPlatform::InitDWriteSupport() {
425 mozilla::ScopedGfxFeatureReporter reporter("DWrite");
426 if (!gfxDWriteFont::InitDWriteSupport()) {
427 return false;
430 reporter.SetSuccessful();
431 return true;
434 bool gfxWindowsPlatform::HandleDeviceReset() {
435 DeviceResetReason resetReason = DeviceResetReason::OK;
436 if (!DidRenderingDeviceReset(&resetReason)) {
437 return false;
440 if (resetReason != DeviceResetReason::FORCED_RESET) {
441 Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON,
442 uint32_t(resetReason));
445 // Remove devices and adapters.
446 DeviceManagerDx::Get()->ResetDevices();
448 imgLoader::NormalLoader()->ClearCache(true);
449 imgLoader::NormalLoader()->ClearCache(false);
450 imgLoader::PrivateBrowsingLoader()->ClearCache(true);
451 imgLoader::PrivateBrowsingLoader()->ClearCache(false);
452 gfxAlphaBoxBlur::ShutdownBlurCache();
454 gfxConfig::Reset(Feature::D3D11_COMPOSITING);
455 gfxConfig::Reset(Feature::D3D11_HW_ANGLE);
456 gfxConfig::Reset(Feature::DIRECT2D);
458 InitializeConfig();
459 // XXX Add InitWebRenderConfig() calling.
460 if (mInitializedDevices) {
461 InitGPUProcessSupport();
462 InitializeDevices();
464 UpdateANGLEConfig();
465 return true;
468 BackendPrefsData gfxWindowsPlatform::GetBackendPrefs() const {
469 BackendPrefsData data;
471 data.mCanvasBitmask = BackendTypeBit(BackendType::SKIA);
472 data.mContentBitmask = BackendTypeBit(BackendType::SKIA);
473 data.mCanvasDefault = BackendType::SKIA;
474 data.mContentDefault = BackendType::SKIA;
476 if (gfxConfig::IsEnabled(Feature::DIRECT2D)) {
477 data.mCanvasBitmask |= BackendTypeBit(BackendType::DIRECT2D1_1);
478 data.mCanvasDefault = BackendType::DIRECT2D1_1;
480 return data;
483 void gfxWindowsPlatform::UpdateBackendPrefs() {
484 BackendPrefsData data = GetBackendPrefs();
485 // Remove DIRECT2D1 preference if D2D1Device does not exist.
486 if (!Factory::HasD2D1Device()) {
487 data.mContentBitmask &= ~BackendTypeBit(BackendType::DIRECT2D1_1);
488 if (data.mContentDefault == BackendType::DIRECT2D1_1) {
489 data.mContentDefault = BackendType::SKIA;
492 // Don't exclude DIRECT2D1_1 if using remote canvas, because DIRECT2D1_1 and
493 // hence the device will be used in the GPU process.
494 if (!gfxPlatform::UseRemoteCanvas()) {
495 data.mCanvasBitmask &= ~BackendTypeBit(BackendType::DIRECT2D1_1);
496 if (data.mCanvasDefault == BackendType::DIRECT2D1_1) {
497 data.mCanvasDefault = BackendType::SKIA;
501 InitBackendPrefs(std::move(data));
504 bool gfxWindowsPlatform::IsDirect2DBackend() {
505 return GetDefaultContentBackend() == BackendType::DIRECT2D1_1;
508 void gfxWindowsPlatform::UpdateRenderMode() {
509 bool didReset = HandleDeviceReset();
511 UpdateBackendPrefs();
513 if (didReset) {
514 mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(
515 IntSize(1, 1), SurfaceFormat::B8G8R8A8);
516 if (!mScreenReferenceDrawTarget) {
517 gfxCriticalNote
518 << "Failed to update reference draw target after device reset"
519 << ", D3D11 device:" << hexa(Factory::GetDirect3D11Device().get())
520 << ", D3D11 status:"
521 << FeatureStatusToString(
522 gfxConfig::GetValue(Feature::D3D11_COMPOSITING))
523 << ", D2D1 device:" << hexa(Factory::GetD2D1Device().get())
524 << ", D2D1 status:"
525 << FeatureStatusToString(gfxConfig::GetValue(Feature::DIRECT2D))
526 << ", content:" << int(GetDefaultContentBackend())
527 << ", compositor:" << int(GetCompositorBackend());
528 MOZ_CRASH(
529 "GFX: Failed to update reference draw target after device reset");
534 void gfxWindowsPlatform::UpdateSupportsHDR() {
535 // TODO: This function crashes content processes, for reasons that are not
536 // obvious from the crash reports. For now, this function can only be executed
537 // by the parent process. Therefore SupportsHDR() will always return false for
538 // content processes, as noted in the header.
539 if (!XRE_IsParentProcess()) {
540 return;
543 // Set mSupportsHDR to true if any of the DeviceManager outputs have both:
544 // 1) greater than 8-bit color
545 // 2) a colorspace that uses BT2020
546 DeviceManagerDx* dx = DeviceManagerDx::Get();
547 nsTArray<DXGI_OUTPUT_DESC1> outputs = dx->EnumerateOutputs();
549 for (auto& output : outputs) {
550 if (output.BitsPerColor <= 8) {
551 continue;
554 switch (output.ColorSpace) {
555 case DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020:
556 case DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020:
557 case DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020:
558 case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020:
559 case DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020:
560 case DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020:
561 case DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020:
562 case DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020:
563 case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020:
564 case DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020:
565 case DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020:
566 #ifndef __MINGW32__
567 // Windows MinGW has an older dxgicommon.h that doesn't define
568 // these enums. We'd like to define them ourselves in that case,
569 // but there's no compilable way to add new enums to an existing
570 // enum type. So instead we just don't check for these values.
571 case DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020:
572 case DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020:
573 case DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020:
574 #endif
575 mSupportsHDR = true;
576 return;
577 default:
578 break;
582 mSupportsHDR = false;
585 mozilla::gfx::BackendType gfxWindowsPlatform::GetContentBackendFor(
586 mozilla::layers::LayersBackend aLayers) {
587 mozilla::gfx::BackendType defaultBackend =
588 gfxPlatform::GetDefaultContentBackend();
589 if (aLayers == LayersBackend::LAYERS_WR &&
590 gfx::gfxVars::UseWebRenderANGLE()) {
591 return defaultBackend;
594 if (defaultBackend == BackendType::DIRECT2D1_1) {
595 // We can't have D2D without D3D11 layers, so fallback to Skia.
596 return BackendType::SKIA;
599 // Otherwise we have some non-accelerated backend and that's ok.
600 return defaultBackend;
603 mozilla::gfx::BackendType gfxWindowsPlatform::GetPreferredCanvasBackend() {
604 mozilla::gfx::BackendType backend = gfxPlatform::GetPreferredCanvasBackend();
606 if (backend == BackendType::DIRECT2D1_1) {
607 if (!gfx::gfxVars::UseWebRenderANGLE()) {
608 // We can't have D2D without ANGLE when WebRender is enabled, so fallback
609 // to Skia.
610 return BackendType::SKIA;
613 // Fall back to software when remote canvas has been deactivated.
614 if (CanvasChild::Deactivated()) {
615 return BackendType::SKIA;
618 return backend;
621 bool gfxWindowsPlatform::CreatePlatformFontList() {
622 if (DWriteEnabled()) {
623 if (gfxPlatformFontList::Initialize(new gfxDWriteFontList)) {
624 return true;
627 // DWrite font initialization failed! Don't know why this would happen,
628 // but apparently it can - see bug 594865.
629 // So we're going to fall back to GDI fonts & rendering.
630 DisableD2D(FeatureStatus::Failed, "Failed to initialize fonts",
631 "FEATURE_FAILURE_FONT_FAIL"_ns);
634 // Make sure the static variable is initialized...
635 gfxPlatform::HasVariationFontSupport();
636 // ...then force it to false, even if the Windows version was recent enough
637 // to permit it, as we're using GDI fonts.
638 sHasVariationFontSupport = false;
640 return gfxPlatformFontList::Initialize(new gfxGDIFontList);
643 // This function will permanently disable D2D for the session. It's intended to
644 // be used when, after initially chosing to use Direct2D, we encounter a
645 // scenario we can't support.
647 // This is called during gfxPlatform::Init() so at this point there should be no
648 // DrawTargetD2D/1 instances.
649 void gfxWindowsPlatform::DisableD2D(FeatureStatus aStatus, const char* aMessage,
650 const nsACString& aFailureId) {
651 gfxConfig::SetFailed(Feature::DIRECT2D, aStatus, aMessage, aFailureId);
652 Factory::SetDirect3D11Device(nullptr);
653 UpdateBackendPrefs();
656 already_AddRefed<gfxASurface> gfxWindowsPlatform::CreateOffscreenSurface(
657 const IntSize& aSize, gfxImageFormat aFormat) {
658 if (!Factory::AllowedSurfaceSize(aSize)) {
659 return nullptr;
662 RefPtr<gfxASurface> surf = nullptr;
664 #ifdef CAIRO_HAS_WIN32_SURFACE
665 if (!XRE_IsContentProcess()) {
666 if (mRenderMode == RENDER_GDI || mRenderMode == RENDER_DIRECT2D) {
667 surf = new gfxWindowsSurface(aSize, aFormat);
670 #endif
672 if (!surf || surf->CairoStatus()) {
673 surf = new gfxImageSurface(aSize, aFormat);
676 return surf.forget();
679 static const char kFontAparajita[] = "Aparajita";
680 static const char kFontArabicTypesetting[] = "Arabic Typesetting";
681 static const char kFontArial[] = "Arial";
682 static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
683 static const char kFontCambria[] = "Cambria";
684 static const char kFontCambriaMath[] = "Cambria Math";
685 static const char kFontEbrima[] = "Ebrima";
686 static const char kFontEstrangeloEdessa[] = "Estrangelo Edessa";
687 static const char kFontEuphemia[] = "Euphemia";
688 static const char kFontGabriola[] = "Gabriola";
689 static const char kFontJavaneseText[] = "Javanese Text";
690 static const char kFontKhmerUI[] = "Khmer UI";
691 static const char kFontLaoUI[] = "Lao UI";
692 static const char kFontLeelawadeeUI[] = "Leelawadee UI";
693 static const char kFontLucidaSansUnicode[] = "Lucida Sans Unicode";
694 static const char kFontMVBoli[] = "MV Boli";
695 static const char kFontMalgunGothic[] = "Malgun Gothic";
696 static const char kFontMicrosoftJhengHei[] = "Microsoft JhengHei";
697 static const char kFontMicrosoftNewTaiLue[] = "Microsoft New Tai Lue";
698 static const char kFontMicrosoftPhagsPa[] = "Microsoft PhagsPa";
699 static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le";
700 static const char kFontMicrosoftUighur[] = "Microsoft Uighur";
701 static const char kFontMicrosoftYaHei[] = "Microsoft YaHei";
702 static const char kFontMicrosoftYiBaiti[] = "Microsoft Yi Baiti";
703 static const char kFontMeiryo[] = "Meiryo";
704 static const char kFontMongolianBaiti[] = "Mongolian Baiti";
705 static const char kFontMyanmarText[] = "Myanmar Text";
706 static const char kFontNirmalaUI[] = "Nirmala UI";
707 static const char kFontNyala[] = "Nyala";
708 static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
709 static const char kFontSegoeUI[] = "Segoe UI";
710 static const char kFontSegoeUIEmoji[] = "Segoe UI Emoji";
711 static const char kFontSegoeUISymbol[] = "Segoe UI Symbol";
712 static const char kFontSylfaen[] = "Sylfaen";
713 static const char kFontTraditionalArabic[] = "Traditional Arabic";
714 static const char kFontTwemojiMozilla[] = "Twemoji Mozilla";
715 static const char kFontUtsaah[] = "Utsaah";
716 static const char kFontYuGothic[] = "Yu Gothic";
718 void gfxWindowsPlatform::GetCommonFallbackFonts(
719 uint32_t aCh, Script aRunScript, eFontPresentation aPresentation,
720 nsTArray<const char*>& aFontList) {
721 if (PrefersColor(aPresentation)) {
722 aFontList.AppendElement(kFontSegoeUIEmoji);
723 aFontList.AppendElement(kFontTwemojiMozilla);
726 // Arial is used as the default fallback for system fallback
727 aFontList.AppendElement(kFontArial);
729 if (!IS_IN_BMP(aCh)) {
730 uint32_t p = aCh >> 16;
731 if (p == 1) { // SMP plane
732 aFontList.AppendElement(kFontSegoeUISymbol);
733 aFontList.AppendElement(kFontEbrima);
734 aFontList.AppendElement(kFontNirmalaUI);
735 aFontList.AppendElement(kFontCambriaMath);
737 } else {
738 uint32_t b = (aCh >> 8) & 0xff;
740 switch (b) {
741 case 0x05:
742 aFontList.AppendElement(kFontEstrangeloEdessa);
743 aFontList.AppendElement(kFontCambria);
744 break;
745 case 0x06:
746 aFontList.AppendElement(kFontMicrosoftUighur);
747 break;
748 case 0x07:
749 aFontList.AppendElement(kFontEstrangeloEdessa);
750 aFontList.AppendElement(kFontMVBoli);
751 aFontList.AppendElement(kFontEbrima);
752 break;
753 case 0x09:
754 aFontList.AppendElement(kFontNirmalaUI);
755 aFontList.AppendElement(kFontUtsaah);
756 aFontList.AppendElement(kFontAparajita);
757 break;
758 case 0x0a:
759 case 0x0b:
760 case 0x0c:
761 case 0x0d:
762 aFontList.AppendElement(kFontNirmalaUI);
763 break;
764 case 0x0e:
765 aFontList.AppendElement(kFontLaoUI);
766 aFontList.AppendElement(kFontLeelawadeeUI);
767 break;
768 case 0x10:
769 aFontList.AppendElement(kFontMyanmarText);
770 break;
771 case 0x11:
772 aFontList.AppendElement(kFontMalgunGothic);
773 break;
774 case 0x12:
775 case 0x13:
776 aFontList.AppendElement(kFontNyala);
777 aFontList.AppendElement(kFontPlantagenetCherokee);
778 break;
779 case 0x14:
780 case 0x15:
781 case 0x16:
782 aFontList.AppendElement(kFontEuphemia);
783 aFontList.AppendElement(kFontSegoeUISymbol);
784 break;
785 case 0x17:
786 aFontList.AppendElement(kFontKhmerUI);
787 aFontList.AppendElement(kFontLeelawadeeUI);
788 break;
789 case 0x18: // Mongolian
790 aFontList.AppendElement(kFontMongolianBaiti);
791 aFontList.AppendElement(kFontEuphemia);
792 break;
793 case 0x19:
794 aFontList.AppendElement(kFontMicrosoftTaiLe);
795 aFontList.AppendElement(kFontMicrosoftNewTaiLue);
796 aFontList.AppendElement(kFontKhmerUI);
797 aFontList.AppendElement(kFontLeelawadeeUI);
798 break;
799 case 0x1a:
800 aFontList.AppendElement(kFontLeelawadeeUI);
801 break;
802 case 0x1c:
803 aFontList.AppendElement(kFontNirmalaUI);
804 break;
805 case 0x20: // Symbol ranges
806 case 0x21:
807 case 0x22:
808 case 0x23:
809 case 0x24:
810 case 0x25:
811 case 0x26:
812 case 0x27:
813 case 0x29:
814 case 0x2a:
815 case 0x2b:
816 case 0x2c:
817 aFontList.AppendElement(kFontSegoeUI);
818 aFontList.AppendElement(kFontSegoeUISymbol);
819 aFontList.AppendElement(kFontCambria);
820 aFontList.AppendElement(kFontMeiryo);
821 aFontList.AppendElement(kFontArial);
822 aFontList.AppendElement(kFontLucidaSansUnicode);
823 aFontList.AppendElement(kFontEbrima);
824 break;
825 case 0x2d:
826 case 0x2e:
827 case 0x2f:
828 aFontList.AppendElement(kFontEbrima);
829 aFontList.AppendElement(kFontNyala);
830 aFontList.AppendElement(kFontSegoeUI);
831 aFontList.AppendElement(kFontSegoeUISymbol);
832 aFontList.AppendElement(kFontMeiryo);
833 break;
834 case 0x28: // Braille
835 aFontList.AppendElement(kFontSegoeUISymbol);
836 break;
837 case 0x30:
838 case 0x31:
839 aFontList.AppendElement(kFontMicrosoftYaHei);
840 break;
841 case 0x32:
842 aFontList.AppendElement(kFontMalgunGothic);
843 break;
844 case 0x4d:
845 aFontList.AppendElement(kFontSegoeUISymbol);
846 break;
847 case 0x9f:
848 aFontList.AppendElement(kFontMicrosoftYaHei);
849 aFontList.AppendElement(kFontYuGothic);
850 break;
851 case 0xa0: // Yi
852 case 0xa1:
853 case 0xa2:
854 case 0xa3:
855 case 0xa4:
856 aFontList.AppendElement(kFontMicrosoftYiBaiti);
857 aFontList.AppendElement(kFontSegoeUI);
858 break;
859 case 0xa5:
860 case 0xa6:
861 case 0xa7:
862 aFontList.AppendElement(kFontEbrima);
863 aFontList.AppendElement(kFontSegoeUI);
864 aFontList.AppendElement(kFontCambriaMath);
865 break;
866 case 0xa8:
867 aFontList.AppendElement(kFontMicrosoftPhagsPa);
868 aFontList.AppendElement(kFontNirmalaUI);
869 break;
870 case 0xa9:
871 aFontList.AppendElement(kFontMalgunGothic);
872 aFontList.AppendElement(kFontJavaneseText);
873 aFontList.AppendElement(kFontLeelawadeeUI);
874 break;
875 case 0xaa:
876 aFontList.AppendElement(kFontMyanmarText);
877 break;
878 case 0xab:
879 aFontList.AppendElement(kFontEbrima);
880 aFontList.AppendElement(kFontNyala);
881 break;
882 case 0xd7:
883 aFontList.AppendElement(kFontMalgunGothic);
884 break;
885 case 0xfb:
886 aFontList.AppendElement(kFontMicrosoftUighur);
887 aFontList.AppendElement(kFontGabriola);
888 aFontList.AppendElement(kFontSylfaen);
889 break;
890 case 0xfc:
891 case 0xfd:
892 aFontList.AppendElement(kFontTraditionalArabic);
893 aFontList.AppendElement(kFontArabicTypesetting);
894 break;
895 case 0xfe:
896 aFontList.AppendElement(kFontTraditionalArabic);
897 aFontList.AppendElement(kFontMicrosoftJhengHei);
898 break;
899 case 0xff:
900 aFontList.AppendElement(kFontMicrosoftJhengHei);
901 break;
902 default:
903 break;
907 // Arial Unicode MS has lots of glyphs for obscure characters,
908 // use it as a last resort
909 aFontList.AppendElement(kFontArialUnicodeMS);
911 // If we didn't begin with the color-emoji fonts, include them here
912 // so that they'll be preferred over user-installed (and possibly
913 // broken) fonts in the global fallback path.
914 if (!PrefersColor(aPresentation)) {
915 aFontList.AppendElement(kFontSegoeUIEmoji);
916 aFontList.AppendElement(kFontTwemojiMozilla);
920 bool gfxWindowsPlatform::DidRenderingDeviceReset(
921 DeviceResetReason* aResetReason) {
922 DeviceManagerDx* dm = DeviceManagerDx::Get();
923 if (!dm) {
924 return false;
926 return dm->HasDeviceReset(aResetReason);
929 void gfxWindowsPlatform::CompositorUpdated() {
930 DeviceManagerDx::Get()->ForceDeviceReset(
931 ForcedDeviceResetReason::COMPOSITOR_UPDATED);
932 UpdateRenderMode();
935 BOOL CALLBACK InvalidateWindowForDeviceReset(HWND aWnd, LPARAM aMsg) {
936 RedrawWindow(aWnd, nullptr, nullptr,
937 RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_FRAME);
938 return TRUE;
941 void gfxWindowsPlatform::SchedulePaintIfDeviceReset() {
942 AUTO_PROFILER_LABEL("gfxWindowsPlatform::SchedulePaintIfDeviceReset", OTHER);
944 DeviceResetReason resetReason = DeviceResetReason::OK;
945 if (!DidRenderingDeviceReset(&resetReason)) {
946 return;
949 gfxCriticalNote << "(gfxWindowsPlatform) Detected device reset: "
950 << (int)resetReason;
952 if (XRE_IsParentProcess()) {
953 // Trigger an ::OnPaint for each window.
954 ::EnumThreadWindows(GetCurrentThreadId(), InvalidateWindowForDeviceReset,
956 } else {
957 NS_DispatchToMainThread(NS_NewRunnableFunction(
958 "gfx::gfxWindowsPlatform::SchedulePaintIfDeviceReset", []() -> void {
959 gfxWindowsPlatform::GetPlatform()->CheckForContentOnlyDeviceReset();
960 }));
963 gfxCriticalNote << "(gfxWindowsPlatform) scheduled device update.";
966 void gfxWindowsPlatform::CheckForContentOnlyDeviceReset() {
967 if (!DidRenderingDeviceReset()) {
968 return;
971 bool isContentOnlyTDR;
972 D3D11DeviceStatus status;
974 DeviceManagerDx::Get()->ExportDeviceInfo(&status);
975 CompositorBridgeChild::Get()->SendCheckContentOnlyTDR(status.sequenceNumber(),
976 &isContentOnlyTDR);
978 // The parent process doesn't know about the reset yet, or the reset is
979 // local to our device.
980 if (isContentOnlyTDR) {
981 gfxCriticalNote << "A content-only TDR is detected.";
982 dom::ContentChild* cc = dom::ContentChild::GetSingleton();
983 cc->RecvReinitRenderingForDeviceReset();
987 nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData() {
988 if (XRE_IsContentProcess()) {
989 // This will be passed in during InitChild so we can avoid sending a
990 // sync message back to the parent during init.
991 const mozilla::gfx::ContentDeviceData* contentDeviceData =
992 GetInitContentDeviceData();
993 if (contentDeviceData) {
994 MOZ_ASSERT(!contentDeviceData->cmsOutputProfileData().IsEmpty());
995 return contentDeviceData->cmsOutputProfileData().Clone();
998 // Otherwise we need to ask the parent for the updated color profile
999 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
1000 nsTArray<uint8_t> result;
1001 Unused << cc->SendGetOutputColorProfileData(&result);
1002 return result;
1005 return GetPlatformCMSOutputProfileData_Impl();
1008 nsTArray<uint8_t> gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl() {
1009 static nsTArray<uint8_t> sCached = [&] {
1010 // Check override pref first:
1011 nsTArray<uint8_t> prefProfileData =
1012 gfxPlatform::GetPrefCMSOutputProfileData();
1013 if (!prefProfileData.IsEmpty()) {
1014 return prefProfileData;
1017 // -
1018 // Otherwise, create a dummy DC and pull from that.
1020 HDC dc = ::GetDC(nullptr);
1021 if (!dc) {
1022 return nsTArray<uint8_t>();
1025 WCHAR profilePath[MAX_PATH];
1026 DWORD profilePathLen = MAX_PATH;
1028 bool getProfileResult = ::GetICMProfileW(dc, &profilePathLen, profilePath);
1030 ::ReleaseDC(nullptr, dc);
1032 if (!getProfileResult) {
1033 return nsTArray<uint8_t>();
1036 void* mem = nullptr;
1037 size_t size = 0;
1039 qcms_data_from_unicode_path(profilePath, &mem, &size);
1040 if (!mem) {
1041 return nsTArray<uint8_t>();
1044 nsTArray<uint8_t> result;
1045 result.AppendElements(static_cast<uint8_t*>(mem), size);
1047 free(mem);
1049 return result;
1050 }();
1052 return sCached.Clone();
1055 void gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath,
1056 nsAString& aVersion) {
1057 DWORD versInfoSize, vers[4] = {0};
1058 // version info not available case
1059 aVersion.AssignLiteral(u"0.0.0.0");
1060 versInfoSize = GetFileVersionInfoSizeW(aDLLPath, nullptr);
1061 AutoTArray<BYTE, 512> versionInfo;
1063 if (versInfoSize == 0) {
1064 return;
1067 // XXX(Bug 1631371) Check if this should use a fallible operation as it
1068 // pretended earlier.
1069 versionInfo.AppendElements(uint32_t(versInfoSize));
1071 if (!GetFileVersionInfoW(aDLLPath, 0, versInfoSize,
1072 LPBYTE(versionInfo.Elements()))) {
1073 return;
1076 UINT len = 0;
1077 VS_FIXEDFILEINFO* fileInfo = nullptr;
1078 if (!VerQueryValue(LPBYTE(versionInfo.Elements()), TEXT("\\"),
1079 (LPVOID*)&fileInfo, &len) ||
1080 len == 0 || fileInfo == nullptr) {
1081 return;
1084 DWORD fileVersMS = fileInfo->dwFileVersionMS;
1085 DWORD fileVersLS = fileInfo->dwFileVersionLS;
1087 vers[0] = HIWORD(fileVersMS);
1088 vers[1] = LOWORD(fileVersMS);
1089 vers[2] = HIWORD(fileVersLS);
1090 vers[3] = LOWORD(fileVersLS);
1092 char buf[256];
1093 SprintfLiteral(buf, "%lu.%lu.%lu.%lu", vers[0], vers[1], vers[2], vers[3]);
1094 aVersion.Assign(NS_ConvertUTF8toUTF16(buf));
1097 static BOOL CALLBACK AppendClearTypeParams(HMONITOR aMonitor, HDC, LPRECT,
1098 LPARAM aContext) {
1099 MONITORINFOEXW monitorInfo;
1100 monitorInfo.cbSize = sizeof(MONITORINFOEXW);
1101 if (!GetMonitorInfoW(aMonitor, &monitorInfo)) {
1102 return TRUE;
1105 ClearTypeParameterInfo ctinfo;
1106 ctinfo.displayName.Assign(monitorInfo.szDevice);
1108 RefPtr<IDWriteRenderingParams> renderingParams;
1109 HRESULT hr = Factory::GetDWriteFactory()->CreateMonitorRenderingParams(
1110 aMonitor, getter_AddRefs(renderingParams));
1111 if (FAILED(hr)) {
1112 return TRUE;
1115 ctinfo.gamma = renderingParams->GetGamma() * 1000;
1116 ctinfo.pixelStructure = renderingParams->GetPixelGeometry();
1117 ctinfo.clearTypeLevel = renderingParams->GetClearTypeLevel() * 100;
1118 ctinfo.enhancedContrast = renderingParams->GetEnhancedContrast() * 100;
1120 auto* params = reinterpret_cast<nsTArray<ClearTypeParameterInfo>*>(aContext);
1121 params->AppendElement(ctinfo);
1122 return TRUE;
1125 void gfxWindowsPlatform::GetCleartypeParams(
1126 nsTArray<ClearTypeParameterInfo>& aParams) {
1127 aParams.Clear();
1128 if (!DWriteEnabled()) {
1129 return;
1131 EnumDisplayMonitors(nullptr, nullptr, AppendClearTypeParams,
1132 reinterpret_cast<LPARAM>(&aParams));
1135 void gfxWindowsPlatform::FontsPrefsChanged(const char* aPref) {
1136 bool clearTextFontCaches = true;
1138 gfxPlatform::FontsPrefsChanged(aPref);
1140 if (aPref &&
1141 !strncmp(GFX_CLEARTYPE_PARAMS, aPref, strlen(GFX_CLEARTYPE_PARAMS))) {
1142 gfxDWriteFont::UpdateClearTypeVars();
1143 } else {
1144 clearTextFontCaches = false;
1147 if (clearTextFontCaches) {
1148 gfxFontCache* fc = gfxFontCache::GetCache();
1149 if (fc) {
1150 fc->Flush();
1155 bool gfxWindowsPlatform::IsOptimus() {
1156 static int knowIsOptimus = -1;
1157 if (knowIsOptimus == -1) {
1158 // other potential optimus -- nvd3d9wrapx.dll & nvdxgiwrap.dll
1159 if (GetModuleHandleA("nvumdshim.dll") ||
1160 GetModuleHandleA("nvumdshimx.dll")) {
1161 knowIsOptimus = 1;
1162 } else {
1163 knowIsOptimus = 0;
1166 return knowIsOptimus;
1169 static void InitializeANGLEConfig() {
1170 FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
1172 if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1173 d3d11ANGLE.DisableByDefault(FeatureStatus::Unavailable,
1174 "D3D11 compositing is disabled",
1175 "FEATURE_FAILURE_HW_ANGLE_D3D11_DISABLED"_ns);
1176 return;
1179 d3d11ANGLE.EnableByDefault();
1181 nsCString message;
1182 nsCString failureId;
1183 if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
1184 &message, failureId)) {
1185 d3d11ANGLE.Disable(FeatureStatus::Blocklisted, message.get(), failureId);
1189 void gfxWindowsPlatform::InitializeDirectDrawConfig() {
1190 MOZ_ASSERT(XRE_IsParentProcess());
1192 FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
1193 ddraw.EnableByDefault();
1196 void gfxWindowsPlatform::InitializeConfig() {
1197 if (XRE_IsParentProcess()) {
1198 // The parent process first determines which features can be attempted.
1199 // This information is relayed to content processes and the GPU process.
1200 InitializeD3D11Config();
1201 InitializeANGLEConfig();
1202 InitializeD2DConfig();
1203 } else {
1204 ImportCachedContentDeviceData();
1205 InitializeANGLEConfig();
1209 void gfxWindowsPlatform::InitializeD3D11Config() {
1210 MOZ_ASSERT(XRE_IsParentProcess());
1212 FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
1214 if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
1215 d3d11.DisableByDefault(FeatureStatus::Unavailable,
1216 "Hardware compositing is disabled",
1217 "FEATURE_FAILURE_D3D11_NEED_HWCOMP"_ns);
1218 return;
1221 d3d11.EnableByDefault();
1223 // Check if the user really, really wants WARP.
1224 if (StaticPrefs::layers_d3d11_force_warp_AtStartup()) {
1225 // Force D3D11 on even if we disabled it.
1226 d3d11.UserForceEnable("User force-enabled WARP");
1229 nsCString message;
1230 nsCString failureId;
1231 if (StaticPrefs::layers_d3d11_enable_blacklist_AtStartup() &&
1232 !gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
1233 &message, failureId)) {
1234 d3d11.Disable(FeatureStatus::Blocklisted, message.get(), failureId);
1238 /* static */
1239 void gfxWindowsPlatform::RecordContentDeviceFailure(
1240 TelemetryDeviceCode aDevice) {
1241 // If the parent process fails to acquire a device, we record this
1242 // normally as part of the environment. The exceptional case we're
1243 // looking for here is when the parent process successfully acquires
1244 // a device, but the content process fails to acquire the same device.
1245 // This would not normally be displayed in about:support.
1246 if (!XRE_IsContentProcess()) {
1247 return;
1249 Telemetry::Accumulate(Telemetry::GFX_CONTENT_FAILED_TO_ACQUIRE_DEVICE,
1250 uint32_t(aDevice));
1253 void gfxWindowsPlatform::RecordStartupTelemetry() {
1254 if (!XRE_IsParentProcess()) {
1255 return;
1258 DeviceManagerDx* dx = DeviceManagerDx::Get();
1259 nsTArray<DXGI_OUTPUT_DESC1> outputs = dx->EnumerateOutputs();
1261 uint32_t allSupportedColorSpaces = 0;
1262 for (auto& output : outputs) {
1263 uint32_t colorSpace = 1 << output.ColorSpace;
1264 allSupportedColorSpaces |= colorSpace;
1267 Telemetry::ScalarSet(
1268 Telemetry::ScalarID::GFX_HDR_WINDOWS_DISPLAY_COLORSPACE_BITFIELD,
1269 allSupportedColorSpaces);
1272 // Supports lazy device initialization on Windows, so that WebRender can avoid
1273 // initializing GPU state and allocating swap chains for most non-GPU processes.
1274 void gfxWindowsPlatform::EnsureDevicesInitialized() {
1275 MOZ_DIAGNOSTIC_ASSERT(!IsWin32kLockedDown());
1277 if (!mInitializedDevices) {
1278 mInitializedDevices = true;
1279 InitializeDevices();
1280 UpdateBackendPrefs();
1284 bool gfxWindowsPlatform::DevicesInitialized() { return mInitializedDevices; }
1286 void gfxWindowsPlatform::InitializeDevices() {
1287 MOZ_ASSERT(NS_IsMainThread());
1289 if (XRE_IsParentProcess()) {
1290 // If we're the UI process, and the GPU process is enabled, then we don't
1291 // initialize any DirectX devices. We do leave them enabled in gfxConfig
1292 // though. If the GPU process fails to create these devices it will send
1293 // a message back and we'll update their status.
1294 if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
1295 return;
1298 // No GPU process, continue initializing devices as normal.
1301 // If acceleration is disabled, we refuse to initialize anything.
1302 if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
1303 return;
1306 // If we previously crashed initializing devices, bail out now.
1307 D3D11LayersCrashGuard detectCrashes;
1308 if (detectCrashes.Crashed()) {
1309 gfxConfig::SetFailed(Feature::HW_COMPOSITING,
1310 FeatureStatus::CrashedOnStartup,
1311 "Crashed during startup in a previous session");
1312 gfxConfig::SetFailed(
1313 Feature::D3D11_COMPOSITING, FeatureStatus::CrashedOnStartup,
1314 "Harware acceleration crashed during startup in a previous session");
1315 gfxConfig::SetFailed(
1316 Feature::DIRECT2D, FeatureStatus::CrashedOnStartup,
1317 "Harware acceleration crashed during startup in a previous session");
1318 return;
1321 bool shouldUseD2D = gfxConfig::IsEnabled(Feature::DIRECT2D);
1323 // First, initialize D3D11. If this succeeds we attempt to use Direct2D.
1324 InitializeD3D11();
1325 InitializeD2D();
1327 if (!gfxConfig::IsEnabled(Feature::DIRECT2D) && XRE_IsContentProcess() &&
1328 shouldUseD2D) {
1329 RecordContentDeviceFailure(TelemetryDeviceCode::D2D1);
1333 void gfxWindowsPlatform::InitializeD3D11() {
1334 // This function attempts to initialize our D3D11 devices, if the hardware
1335 // is not blocklisted for D3D11 layers. This first attempt will try to create
1336 // a hardware accelerated device. If this creation fails or the hardware is
1337 // blocklisted, then this function will abort if WARP is disabled, causing us
1338 // to fallback to Basic layers. If WARP is not disabled it will use a WARP
1339 // device which should always be available on Windows 7 and higher.
1340 if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1341 return;
1344 DeviceManagerDx* dm = DeviceManagerDx::Get();
1345 if (XRE_IsParentProcess()) {
1346 if (!dm->CreateCompositorDevices()) {
1347 return;
1351 dm->CreateContentDevices();
1353 // Content process failed to create the d3d11 device while parent process
1354 // succeed.
1355 if (XRE_IsContentProcess() &&
1356 !gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1357 gfxCriticalError()
1358 << "[D3D11] Failed to create the D3D11 device in content \
1359 process.";
1363 void gfxWindowsPlatform::InitializeD2DConfig() {
1364 FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
1366 if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1367 d2d1.DisableByDefault(FeatureStatus::Unavailable,
1368 "Direct2D requires Direct3D 11 compositing",
1369 "FEATURE_FAILURE_D2D_D3D11_COMP"_ns);
1370 return;
1373 d2d1.SetDefaultFromPref(StaticPrefs::GetPrefName_gfx_direct2d_disabled(),
1374 false,
1375 StaticPrefs::GetPrefDefault_gfx_direct2d_disabled());
1377 nsCString message;
1378 nsCString failureId;
1379 if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT2D, &message,
1380 failureId)) {
1381 d2d1.Disable(FeatureStatus::Blocklisted, message.get(), failureId);
1384 if (!d2d1.IsEnabled() &&
1385 StaticPrefs::gfx_direct2d_force_enabled_AtStartup()) {
1386 d2d1.UserForceEnable("Force-enabled via user-preference");
1390 void gfxWindowsPlatform::InitializeD2D() {
1391 ScopedGfxFeatureReporter d2d1_1("D2D1.1");
1393 FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
1395 DeviceManagerDx* dm = DeviceManagerDx::Get();
1397 // We don't know this value ahead of time, but the user can force-override
1398 // it, so we use Disable instead of SetFailed.
1399 if (dm->IsWARP()) {
1400 d2d1.Disable(FeatureStatus::Blocked,
1401 "Direct2D is not compatible with Direct3D11 WARP",
1402 "FEATURE_FAILURE_D2D_WARP_BLOCK"_ns);
1405 // If we pass all the initial checks, we can proceed to runtime decisions.
1406 if (!d2d1.IsEnabled()) {
1407 return;
1410 if (!Factory::SupportsD2D1()) {
1411 d2d1.SetFailed(FeatureStatus::Unavailable,
1412 "Failed to acquire a Direct2D 1.1 factory",
1413 "FEATURE_FAILURE_D2D_FACTORY"_ns);
1414 return;
1417 if (!dm->GetContentDevice()) {
1418 d2d1.SetFailed(FeatureStatus::Failed,
1419 "Failed to acquire a Direct3D 11 content device",
1420 "FEATURE_FAILURE_D2D_DEVICE"_ns);
1421 return;
1424 if (!dm->TextureSharingWorks()) {
1425 d2d1.SetFailed(FeatureStatus::Failed,
1426 "Direct3D11 device does not support texture sharing",
1427 "FEATURE_FAILURE_D2D_TXT_SHARING"_ns);
1428 return;
1431 // Using Direct2D depends on DWrite support.
1432 if (!DWriteEnabled() && !InitDWriteSupport()) {
1433 d2d1.SetFailed(FeatureStatus::Failed,
1434 "Failed to initialize DirectWrite support",
1435 "FEATURE_FAILURE_D2D_DWRITE"_ns);
1436 return;
1439 // Verify that Direct2D device creation succeeded.
1440 RefPtr<ID3D11Device> contentDevice = dm->GetContentDevice();
1441 if (!Factory::SetDirect3D11Device(contentDevice)) {
1442 d2d1.SetFailed(FeatureStatus::Failed, "Failed to create a Direct2D device",
1443 "FEATURE_FAILURE_D2D_CREATE_FAILED"_ns);
1444 return;
1447 MOZ_ASSERT(d2d1.IsEnabled());
1448 d2d1_1.SetSuccessful();
1451 void gfxWindowsPlatform::InitGPUProcessSupport() {
1452 FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);
1454 if (!gpuProc.IsEnabled()) {
1455 return;
1458 if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1459 // Don't use the GPU process if not using D3D11, unless software
1460 // compositor is allowed
1461 if (StaticPrefs::layers_gpu_process_allow_software_AtStartup()) {
1462 return;
1464 gpuProc.Disable(FeatureStatus::Unavailable,
1465 "Not using GPU Process since D3D11 is unavailable",
1466 "FEATURE_FAILURE_NO_D3D11"_ns);
1468 // If we're still enabled at this point, the user set the force-enabled pref.
1471 class D3DVsyncSource final : public VsyncSource {
1472 public:
1473 D3DVsyncSource()
1474 : mPrevVsync(TimeStamp::Now()),
1475 mVsyncEnabled(false),
1476 mWaitVBlankMonitor(NULL) {
1477 mVsyncThread = new base::Thread("WindowsVsyncThread");
1478 MOZ_RELEASE_ASSERT(mVsyncThread->Start(),
1479 "GFX: Could not start Windows vsync thread");
1480 SetVsyncRate();
1483 void SetVsyncRate() {
1484 DWM_TIMING_INFO vblankTime;
1485 // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
1486 vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
1487 HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
1488 if (SUCCEEDED(hr)) {
1489 UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
1490 // We get the rate in hertz / time, but we want the rate in ms.
1491 float rate =
1492 ((float)refreshRate.uiDenominator / (float)refreshRate.uiNumerator) *
1493 1000;
1494 mVsyncRate = TimeDuration::FromMilliseconds(rate);
1495 } else {
1496 mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
1500 virtual void Shutdown() override {
1501 MOZ_ASSERT(NS_IsMainThread());
1502 DisableVsync();
1503 mVsyncThread->Stop();
1504 delete mVsyncThread;
1507 virtual void EnableVsync() override {
1508 MOZ_ASSERT(NS_IsMainThread());
1509 MOZ_ASSERT(mVsyncThread->IsRunning());
1510 { // scope lock
1511 if (mVsyncEnabled) {
1512 return;
1514 mVsyncEnabled = true;
1517 mVsyncThread->message_loop()->PostTask(NewRunnableMethod(
1518 "D3DVsyncSource::VBlankLoop", this, &D3DVsyncSource::VBlankLoop));
1521 virtual void DisableVsync() override {
1522 MOZ_ASSERT(NS_IsMainThread());
1523 MOZ_ASSERT(mVsyncThread->IsRunning());
1524 if (!mVsyncEnabled) {
1525 return;
1527 mVsyncEnabled = false;
1530 virtual bool IsVsyncEnabled() override {
1531 MOZ_ASSERT(NS_IsMainThread());
1532 return mVsyncEnabled;
1535 virtual TimeDuration GetVsyncRate() override { return mVsyncRate; }
1537 void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp) {
1538 MOZ_ASSERT(IsInVsyncThread());
1539 NS_WARNING(
1540 "DwmComposition dynamically disabled, falling back to software "
1541 "timers");
1543 TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
1544 TimeDuration delay = nextVsync - TimeStamp::Now();
1545 if (delay.ToMilliseconds() < 0) {
1546 delay = mozilla::TimeDuration::FromMilliseconds(0);
1549 mVsyncThread->message_loop()->PostDelayedTask(
1550 NewRunnableMethod("D3DVsyncSource::VBlankLoop", this,
1551 &D3DVsyncSource::VBlankLoop),
1552 delay.ToMilliseconds());
1555 // Returns the timestamp for the just happened vsync
1556 TimeStamp GetVBlankTime() {
1557 TimeStamp vsync = TimeStamp::Now();
1558 TimeStamp now = vsync;
1560 DWM_TIMING_INFO vblankTime;
1561 // Make sure to init the cbSize, otherwise
1562 // GetCompositionTiming will fail
1563 vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
1564 HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
1565 if (!SUCCEEDED(hr)) {
1566 return vsync;
1569 LARGE_INTEGER frequency;
1570 QueryPerformanceFrequency(&frequency);
1572 LARGE_INTEGER qpcNow;
1573 QueryPerformanceCounter(&qpcNow);
1575 const int microseconds = 1000000;
1576 int64_t adjust = qpcNow.QuadPart - vblankTime.qpcVBlank;
1577 int64_t usAdjust = (adjust * microseconds) / frequency.QuadPart;
1578 vsync -= TimeDuration::FromMicroseconds((double)usAdjust);
1580 // On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
1581 // reports the upcoming vsync time, which is in the future.
1582 // It can also sometimes report a vblank time in the past.
1583 // Since large parts of Gecko assume TimeStamps can't be in future,
1584 // use the previous vsync.
1586 // Windows 10 and Intel HD vsync timestamps are messy and
1587 // all over the place once in a while. Most of the time,
1588 // it reports the upcoming vsync. Sometimes, that upcoming
1589 // vsync is in the past. Sometimes that upcoming vsync is before
1590 // the previously seen vsync.
1591 // In these error cases, normalize to Now();
1592 if (vsync >= now) {
1593 vsync = vsync - mVsyncRate;
1596 // On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
1597 // from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
1598 if (vsync >= now) {
1599 vsync = now;
1602 // Our vsync time is some time very far in the past, adjust to Now.
1603 // 4 ms is arbitrary, so feel free to pick something else if this isn't
1604 // working. See the comment above.
1605 if ((now - vsync).ToMilliseconds() > 4.0) {
1606 vsync = now;
1609 return vsync;
1612 void VBlankLoop() {
1613 MOZ_ASSERT(IsInVsyncThread());
1614 MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));
1616 TimeStamp vsync = TimeStamp::Now();
1617 mPrevVsync = TimeStamp();
1618 TimeStamp flushTime = TimeStamp::Now();
1619 TimeDuration longVBlank = mVsyncRate * 2;
1621 for (;;) {
1622 { // scope lock
1623 if (!mVsyncEnabled) return;
1626 // Large parts of gecko assume that the refresh driver timestamp
1627 // must be <= Now() and cannot be in the future.
1628 MOZ_ASSERT(vsync <= TimeStamp::Now());
1629 NotifyVsync(vsync, vsync + mVsyncRate);
1631 HRESULT hr = E_FAIL;
1632 if (!StaticPrefs::gfx_vsync_force_disable_waitforvblank()) {
1633 UpdateVBlankOutput();
1634 if (mWaitVBlankOutput) {
1635 const TimeStamp vblank_begin_wait = TimeStamp::Now();
1637 AUTO_PROFILER_THREAD_SLEEP;
1638 hr = mWaitVBlankOutput->WaitForVBlank();
1640 if (SUCCEEDED(hr)) {
1641 // vblank might return instantly when running headless,
1642 // monitor powering off, etc. Since we're on a dedicated
1643 // thread, instant-return should not happen in the normal
1644 // case, so catch any odd behavior with a time cutoff:
1645 TimeDuration vblank_wait = TimeStamp::Now() - vblank_begin_wait;
1646 if (vblank_wait.ToMilliseconds() < 1.0) {
1647 hr = E_FAIL; // fall back on old behavior
1652 if (!SUCCEEDED(hr)) {
1653 hr = DwmFlush();
1655 if (!SUCCEEDED(hr)) {
1656 // DWMFlush isn't working, fallback to software vsync.
1657 ScheduleSoftwareVsync(TimeStamp::Now());
1658 return;
1661 TimeStamp now = TimeStamp::Now();
1662 TimeDuration flushDiff = now - flushTime;
1663 flushTime = now;
1664 if ((flushDiff > longVBlank) || mPrevVsync.IsNull()) {
1665 // Our vblank took longer than 2 intervals, readjust our timestamps
1666 vsync = GetVBlankTime();
1667 mPrevVsync = vsync;
1668 } else {
1669 // Instead of giving the actual vsync time, a constant interval
1670 // between vblanks instead of the noise generated via hardware
1671 // is actually what we want. Most apps just care about the diff
1672 // between vblanks to animate, so a clean constant interval is
1673 // smoother.
1674 vsync = mPrevVsync + mVsyncRate;
1675 if (vsync > now) {
1676 // DWMFlush woke up very early, so readjust our times again
1677 vsync = GetVBlankTime();
1680 if (vsync <= mPrevVsync) {
1681 vsync = TimeStamp::Now();
1684 if ((now - vsync).ToMilliseconds() > 2.0) {
1685 // Account for time drift here where vsync never quite catches up to
1686 // Now and we'd fall ever so slightly further behind Now().
1687 vsync = GetVBlankTime();
1690 mPrevVsync = vsync;
1692 } // end for
1694 virtual ~D3DVsyncSource() { MOZ_ASSERT(NS_IsMainThread()); }
1696 private:
1697 bool IsInVsyncThread() {
1698 return mVsyncThread->thread_id() == PlatformThread::CurrentId();
1701 void UpdateVBlankOutput() {
1702 HMONITOR primary_monitor =
1703 MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
1704 if (primary_monitor == mWaitVBlankMonitor && mWaitVBlankOutput) {
1705 return;
1708 mWaitVBlankMonitor = primary_monitor;
1710 RefPtr<IDXGIOutput> output = nullptr;
1711 if (DeviceManagerDx* dx = DeviceManagerDx::Get()) {
1712 if (dx->GetOutputFromMonitor(mWaitVBlankMonitor, &output)) {
1713 mWaitVBlankOutput = output;
1714 return;
1718 // failed to convert a monitor to an output so keep trying
1719 mWaitVBlankOutput = nullptr;
1722 TimeStamp mPrevVsync;
1723 base::Thread* mVsyncThread;
1724 TimeDuration mVsyncRate;
1725 Atomic<bool> mVsyncEnabled;
1727 HMONITOR mWaitVBlankMonitor;
1728 RefPtr<IDXGIOutput> mWaitVBlankOutput;
1729 }; // D3DVsyncSource
1731 already_AddRefed<mozilla::gfx::VsyncSource>
1732 gfxWindowsPlatform::CreateGlobalHardwareVsyncSource() {
1733 MOZ_RELEASE_ASSERT(NS_IsMainThread(), "GFX: Not in main thread.");
1735 RefPtr<VsyncSource> d3dVsyncSource = new D3DVsyncSource();
1736 return d3dVsyncSource.forget();
1739 void gfxWindowsPlatform::ImportGPUDeviceData(
1740 const mozilla::gfx::GPUDeviceData& aData) {
1741 MOZ_ASSERT(XRE_IsParentProcess());
1743 gfxPlatform::ImportGPUDeviceData(aData);
1745 gfxConfig::ImportChange(Feature::D3D11_COMPOSITING, aData.d3d11Compositing());
1747 DeviceManagerDx* dm = DeviceManagerDx::Get();
1748 if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1749 dm->ImportDeviceInfo(aData.gpuDevice().ref());
1750 } else {
1751 // There should be no devices, so this just takes away the device status.
1752 dm->ResetDevices();
1754 // Make sure we disable D2D if content processes might use it.
1755 FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
1756 if (d2d1.IsEnabled()) {
1757 d2d1.SetFailed(FeatureStatus::Unavailable,
1758 "Direct2D requires Direct3D 11 compositing",
1759 "FEATURE_FAILURE_D2D_D3D11_COMP"_ns);
1763 // CanUseHardwareVideoDecoding depends on d3d11 state, so update
1764 // the cached value now.
1765 UpdateCanUseHardwareVideoDecoding();
1767 // For completeness (and messaging in about:support). Content recomputes this
1768 // on its own, and we won't use ANGLE in the UI process if we're using a GPU
1769 // process.
1770 UpdateANGLEConfig();
1773 void gfxWindowsPlatform::ImportContentDeviceData(
1774 const mozilla::gfx::ContentDeviceData& aData) {
1775 MOZ_ASSERT(XRE_IsContentProcess());
1777 gfxPlatform::ImportContentDeviceData(aData);
1779 const DevicePrefs& prefs = aData.prefs();
1780 gfxConfig::Inherit(Feature::D3D11_COMPOSITING, prefs.d3d11Compositing());
1781 gfxConfig::Inherit(Feature::DIRECT2D, prefs.useD2D1());
1783 if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1784 DeviceManagerDx* dm = DeviceManagerDx::Get();
1785 dm->ImportDeviceInfo(aData.d3d11());
1788 // aData->cmsOutputProfileData() will be read during color profile init,
1789 // not as part of this import function
1792 void gfxWindowsPlatform::BuildContentDeviceData(ContentDeviceData* aOut) {
1793 // Check for device resets before giving back new graphics information.
1794 UpdateRenderMode();
1796 gfxPlatform::BuildContentDeviceData(aOut);
1798 const FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
1799 aOut->prefs().d3d11Compositing() = d3d11.GetValue();
1800 aOut->prefs().useD2D1() = gfxConfig::GetValue(Feature::DIRECT2D);
1802 if (d3d11.IsEnabled()) {
1803 DeviceManagerDx* dm = DeviceManagerDx::Get();
1804 dm->ExportDeviceInfo(&aOut->d3d11());
1807 aOut->cmsOutputProfileData() =
1808 gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfileData();
1811 bool gfxWindowsPlatform::CheckVariationFontSupport() {
1812 // Variation font support is only available on Fall Creators Update or later.
1813 return IsWin10FallCreatorsUpdateOrLater();
1816 void gfxWindowsPlatform::GetPlatformDisplayInfo(
1817 mozilla::widget::InfoObject& aObj) {
1818 HwStretchingSupport stretch;
1819 DeviceManagerDx::Get()->CheckHardwareStretchingSupport(stretch);
1821 nsPrintfCString stretchValue(
1822 "both=%u window-only=%u full-screen-only=%u none=%u error=%u",
1823 stretch.mBoth, stretch.mWindowOnly, stretch.mFullScreenOnly,
1824 stretch.mNone, stretch.mError);
1825 aObj.DefineProperty("HardwareStretching", stretchValue.get());
1827 ScaledResolutionSet scaled;
1828 GetScaledResolutions(scaled);
1829 if (scaled.IsEmpty()) {
1830 return;
1833 aObj.DefineProperty("ScaledResolutionCount", scaled.Length());
1834 for (size_t i = 0; i < scaled.Length(); ++i) {
1835 auto& s = scaled[i];
1836 nsPrintfCString name("ScaledResolution%zu", i);
1837 nsPrintfCString value("source %dx%d, target %dx%d", s.first.width,
1838 s.first.height, s.second.width, s.second.height);
1839 aObj.DefineProperty(name.get(), value.get());