Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#define INITGUID // set before devguid.h
8
9
#include "gfxWindowsPlatform.h"
10
11
#include "cairo.h"
12
#include "mozilla/ArrayUtils.h"
13
#include "mozilla/layers/CompositorBridgeChild.h"
14
15
#include "gfxImageSurface.h"
16
#include "gfxWindowsSurface.h"
17
18
#include "nsUnicharUtils.h"
19
#include "nsUnicodeProperties.h"
20
21
#include "mozilla/Preferences.h"
22
#include "mozilla/Services.h"
23
#include "mozilla/Sprintf.h"
24
#include "mozilla/WindowsVersion.h"
25
#include "nsIGfxInfo.h"
26
#include "nsServiceManagerUtils.h"
27
#include "nsTArray.h"
28
#include "nsThreadUtils.h"
29
#include "mozilla/Telemetry.h"
30
#include "GeckoProfiler.h"
31
32
#include "plbase64.h"
33
#include "nsIXULRuntime.h"
34
#include "imgLoader.h"
35
36
#include "nsIGfxInfo.h"
37
38
#include "gfxCrashReporterUtils.h"
39
40
#include "gfxGDIFontList.h"
41
#include "gfxGDIFont.h"
42
43
#include "mozilla/layers/CompositorThread.h"
44
#include "mozilla/layers/PaintThread.h"
45
#include "mozilla/layers/ReadbackManagerD3D11.h"
46
47
#include "gfxDWriteFontList.h"
48
#include "gfxDWriteFonts.h"
49
#include "gfxDWriteCommon.h"
50
#include <dwrite.h>
51
52
#include "gfxTextRun.h"
53
#include "gfxUserFontSet.h"
54
#include "nsWindowsHelpers.h"
55
#include "gfx2DGlue.h"
56
57
#include <string>
58
59
#include <d3d10_1.h>
60
61
#include "mozilla/gfx/2D.h"
62
#include "mozilla/gfx/gfxVars.h"
63
64
#include "nsMemory.h"
65
66
#include <dwmapi.h>
67
#include <d3d11.h>
68
#include <d2d1_1.h>
69
70
#include "nsIMemoryReporter.h"
71
#include <winternl.h>
72
#include "d3dkmtQueryStatistics.h"
73
74
#include "base/thread.h"
75
#include "mozilla/StaticPrefs_gfx.h"
76
#include "mozilla/StaticPrefs_layers.h"
77
#include "gfxConfig.h"
78
#include "VsyncSource.h"
79
#include "DriverCrashGuard.h"
80
#include "mozilla/dom/ContentChild.h"
81
#include "mozilla/gfx/DeviceManagerDx.h"
82
#include "mozilla/layers/DeviceAttachmentsD3D11.h"
83
#include "D3D11Checks.h"
84
85
#include <devguid.h> // for GUID_DEVCLASS_BATTERY
86
#include <setupapi.h> // for SetupDi*
87
#include <winioctl.h> // for IOCTL_*
88
#include <batclass.h> // for BATTERY_*
89
90
using namespace mozilla;
91
using namespace mozilla::gfx;
92
using namespace mozilla::layers;
93
using namespace mozilla::widget;
94
using namespace mozilla::image;
95
using namespace mozilla::unicode;
96
97
DCForMetrics::DCForMetrics() {
98
// Get the whole screen DC:
99
mDC = GetDC(nullptr);
100
SetGraphicsMode(mDC, GM_ADVANCED);
101
}
102
103
class GfxD2DVramReporter final : public nsIMemoryReporter {
104
~GfxD2DVramReporter() {}
105
106
public:
107
NS_DECL_ISUPPORTS
108
109
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
110
nsISupports* aData, bool aAnonymize) override {
111
MOZ_COLLECT_REPORT("gfx-d2d-vram-draw-target", KIND_OTHER, UNITS_BYTES,
112
Factory::GetD2DVRAMUsageDrawTarget(),
113
"Video memory used by D2D DrawTargets.");
114
115
MOZ_COLLECT_REPORT("gfx-d2d-vram-source-surface", KIND_OTHER, UNITS_BYTES,
116
Factory::GetD2DVRAMUsageSourceSurface(),
117
"Video memory used by D2D SourceSurfaces.");
118
119
return NS_OK;
120
}
121
};
122
123
NS_IMPL_ISUPPORTS(GfxD2DVramReporter, nsIMemoryReporter)
124
125
#define GFX_CLEARTYPE_PARAMS "gfx.font_rendering.cleartype_params."
126
#define GFX_CLEARTYPE_PARAMS_GAMMA "gfx.font_rendering.cleartype_params.gamma"
127
#define GFX_CLEARTYPE_PARAMS_CONTRAST \
128
"gfx.font_rendering.cleartype_params.enhanced_contrast"
129
#define GFX_CLEARTYPE_PARAMS_LEVEL \
130
"gfx.font_rendering.cleartype_params.cleartype_level"
131
#define GFX_CLEARTYPE_PARAMS_STRUCTURE \
132
"gfx.font_rendering.cleartype_params.pixel_structure"
133
#define GFX_CLEARTYPE_PARAMS_MODE \
134
"gfx.font_rendering.cleartype_params.rendering_mode"
135
136
class GPUAdapterReporter final : public nsIMemoryReporter {
137
// Callers must Release the DXGIAdapter after use or risk mem-leak
138
static bool GetDXGIAdapter(IDXGIAdapter** aDXGIAdapter) {
139
RefPtr<ID3D11Device> d3d11Device;
140
RefPtr<IDXGIDevice> dxgiDevice;
141
bool result = false;
142
143
if ((d3d11Device = mozilla::gfx::Factory::GetDirect3D11Device())) {
144
if (d3d11Device->QueryInterface(__uuidof(IDXGIDevice),
145
getter_AddRefs(dxgiDevice)) == S_OK) {
146
result = (dxgiDevice->GetAdapter(aDXGIAdapter) == S_OK);
147
}
148
}
149
150
return result;
151
}
152
153
~GPUAdapterReporter() {}
154
155
public:
156
NS_DECL_ISUPPORTS
157
158
NS_IMETHOD
159
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
160
bool aAnonymize) override {
161
HANDLE ProcessHandle = GetCurrentProcess();
162
163
int64_t dedicatedBytesUsed = 0;
164
int64_t sharedBytesUsed = 0;
165
int64_t committedBytesUsed = 0;
166
IDXGIAdapter* DXGIAdapter;
167
168
HMODULE gdi32Handle;
169
PFND3DKMTQS queryD3DKMTStatistics = nullptr;
170
171
if ((gdi32Handle = LoadLibrary(TEXT("gdi32.dll"))))
172
queryD3DKMTStatistics =
173
(PFND3DKMTQS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");
174
175
if (queryD3DKMTStatistics && GetDXGIAdapter(&DXGIAdapter)) {
176
// Most of this block is understood thanks to wj32's work on Process
177
// Hacker
178
179
DXGI_ADAPTER_DESC adapterDesc;
180
D3DKMTQS queryStatistics;
181
182
DXGIAdapter->GetDesc(&adapterDesc);
183
DXGIAdapter->Release();
184
185
memset(&queryStatistics, 0, sizeof(D3DKMTQS));
186
queryStatistics.Type = D3DKMTQS_PROCESS;
187
queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
188
queryStatistics.hProcess = ProcessHandle;
189
if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
190
committedBytesUsed =
191
queryStatistics.QueryResult.ProcessInfo.SystemMemory.BytesAllocated;
192
}
193
194
memset(&queryStatistics, 0, sizeof(D3DKMTQS));
195
queryStatistics.Type = D3DKMTQS_ADAPTER;
196
queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
197
if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
198
ULONG i;
199
ULONG segmentCount = queryStatistics.QueryResult.AdapterInfo.NbSegments;
200
201
for (i = 0; i < segmentCount; i++) {
202
memset(&queryStatistics, 0, sizeof(D3DKMTQS));
203
queryStatistics.Type = D3DKMTQS_SEGMENT;
204
queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
205
queryStatistics.QuerySegment.SegmentId = i;
206
207
if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
208
bool aperture;
209
210
// SegmentInformation has a different definition in Win7 than later
211
// versions
212
if (!IsWin8OrLater())
213
aperture = queryStatistics.QueryResult.SegmentInfoWin7.Aperture;
214
else
215
aperture = queryStatistics.QueryResult.SegmentInfoWin8.Aperture;
216
217
memset(&queryStatistics, 0, sizeof(D3DKMTQS));
218
queryStatistics.Type = D3DKMTQS_PROCESS_SEGMENT;
219
queryStatistics.AdapterLuid = adapterDesc.AdapterLuid;
220
queryStatistics.hProcess = ProcessHandle;
221
queryStatistics.QueryProcessSegment.SegmentId = i;
222
if (NT_SUCCESS(queryD3DKMTStatistics(&queryStatistics))) {
223
ULONGLONG bytesCommitted;
224
if (!IsWin8OrLater())
225
bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInfo
226
.Win7.BytesCommitted;
227
else
228
bytesCommitted = queryStatistics.QueryResult.ProcessSegmentInfo
229
.Win8.BytesCommitted;
230
if (aperture)
231
sharedBytesUsed += bytesCommitted;
232
else
233
dedicatedBytesUsed += bytesCommitted;
234
}
235
}
236
}
237
}
238
}
239
240
FreeLibrary(gdi32Handle);
241
242
MOZ_COLLECT_REPORT("gpu-committed", KIND_OTHER, UNITS_BYTES,
243
committedBytesUsed,
244
"Memory committed by the Windows graphics system.");
245
246
MOZ_COLLECT_REPORT(
247
"gpu-dedicated", KIND_OTHER, UNITS_BYTES, dedicatedBytesUsed,
248
"Out-of-process memory allocated for this process in a physical "
249
"GPU adapter's memory.");
250
251
MOZ_COLLECT_REPORT("gpu-shared", KIND_OTHER, UNITS_BYTES, sharedBytesUsed,
252
"In-process memory that is shared with the GPU.");
253
254
return NS_OK;
255
}
256
};
257
258
NS_IMPL_ISUPPORTS(GPUAdapterReporter, nsIMemoryReporter)
259
260
Atomic<size_t> gfxWindowsPlatform::sD3D11SharedTextures;
261
Atomic<size_t> gfxWindowsPlatform::sD3D9SharedTextures;
262
263
class D3DSharedTexturesReporter final : public nsIMemoryReporter {
264
~D3DSharedTexturesReporter() {}
265
266
public:
267
NS_DECL_ISUPPORTS
268
269
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
270
nsISupports* aData, bool aAnonymize) override {
271
if (gfxWindowsPlatform::sD3D11SharedTextures > 0) {
272
MOZ_COLLECT_REPORT("d3d11-shared-textures", KIND_OTHER, UNITS_BYTES,
273
gfxWindowsPlatform::sD3D11SharedTextures,
274
"D3D11 shared textures.");
275
}
276
277
if (gfxWindowsPlatform::sD3D9SharedTextures > 0) {
278
MOZ_COLLECT_REPORT("d3d9-shared-textures", KIND_OTHER, UNITS_BYTES,
279
gfxWindowsPlatform::sD3D9SharedTextures,
280
"D3D9 shared textures.");
281
}
282
283
return NS_OK;
284
}
285
};
286
287
NS_IMPL_ISUPPORTS(D3DSharedTexturesReporter, nsIMemoryReporter)
288
289
gfxWindowsPlatform::gfxWindowsPlatform() : mRenderMode(RENDER_GDI) {
290
/*
291
* Initialize COM
292
*/
293
CoInitialize(nullptr);
294
295
RegisterStrongMemoryReporter(new GfxD2DVramReporter());
296
RegisterStrongMemoryReporter(new GPUAdapterReporter());
297
RegisterStrongMemoryReporter(new D3DSharedTexturesReporter());
298
}
299
300
gfxWindowsPlatform::~gfxWindowsPlatform() {
301
mozilla::gfx::Factory::D2DCleanup();
302
303
DeviceManagerDx::Shutdown();
304
305
/*
306
* Uninitialize COM
307
*/
308
CoUninitialize();
309
}
310
311
static void UpdateANGLEConfig() {
312
if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
313
gfxConfig::Disable(Feature::D3D11_HW_ANGLE, FeatureStatus::Disabled,
314
"D3D11 compositing is disabled");
315
}
316
}
317
318
bool gfxWindowsPlatform::HasBattery() {
319
// Helper classes to manage lifetimes of Windows structs.
320
class MOZ_STACK_CLASS HDevInfoHolder final {
321
public:
322
explicit HDevInfoHolder(HDEVINFO aHandle) : mHandle(aHandle) {}
323
324
~HDevInfoHolder() { ::SetupDiDestroyDeviceInfoList(mHandle); }
325
326
private:
327
HDEVINFO mHandle;
328
};
329
330
class MOZ_STACK_CLASS HandleHolder final {
331
public:
332
explicit HandleHolder(HANDLE aHandle) : mHandle(aHandle) {}
333
334
~HandleHolder() { ::CloseHandle(mHandle); }
335
336
private:
337
HANDLE mHandle;
338
};
339
340
HDEVINFO hdev =
341
::SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, nullptr, nullptr,
342
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
343
if (hdev == INVALID_HANDLE_VALUE) {
344
return true;
345
}
346
347
HDevInfoHolder hdevHolder(hdev);
348
349
DWORD i = 0;
350
SP_DEVICE_INTERFACE_DATA did = {0};
351
did.cbSize = sizeof(did);
352
353
while (::SetupDiEnumDeviceInterfaces(hdev, nullptr, &GUID_DEVCLASS_BATTERY, i,
354
&did)) {
355
DWORD bufferSize = 0;
356
::SetupDiGetDeviceInterfaceDetail(hdev, &did, nullptr, 0, &bufferSize,
357
nullptr);
358
if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
359
return true;
360
}
361
362
UniquePtr<uint8_t[]> buffer(new (std::nothrow) uint8_t[bufferSize]);
363
if (!buffer) {
364
return true;
365
}
366
367
PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd =
368
reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buffer.get());
369
pdidd->cbSize = sizeof(*pdidd);
370
if (!::SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, bufferSize,
371
&bufferSize, nullptr)) {
372
return true;
373
}
374
375
HANDLE hbat = ::CreateFile(pdidd->DevicePath, GENERIC_READ | GENERIC_WRITE,
376
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
377
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
378
if (hbat == INVALID_HANDLE_VALUE) {
379
return true;
380
}
381
382
HandleHolder hbatHolder(hbat);
383
384
BATTERY_QUERY_INFORMATION bqi = {0};
385
DWORD dwWait = 0;
386
DWORD dwOut;
387
388
// We need the tag to query the information below.
389
if (!::DeviceIoControl(hbat, IOCTL_BATTERY_QUERY_TAG, &dwWait,
390
sizeof(dwWait), &bqi.BatteryTag,
391
sizeof(bqi.BatteryTag), &dwOut, nullptr) ||
392
!bqi.BatteryTag) {
393
return true;
394
}
395
396
BATTERY_INFORMATION bi = {0};
397
bqi.InformationLevel = BatteryInformation;
398
399
if (!::DeviceIoControl(hbat, IOCTL_BATTERY_QUERY_INFORMATION, &bqi,
400
sizeof(bqi), &bi, sizeof(bi), &dwOut, nullptr)) {
401
return true;
402
}
403
404
// If a battery intended for general use (i.e. system use) is not a UPS
405
// (i.e. short term), then we know for certain we have a battery.
406
if ((bi.Capabilities & BATTERY_SYSTEM_BATTERY) &&
407
!(bi.Capabilities & BATTERY_IS_SHORT_TERM)) {
408
return true;
409
}
410
411
// Otherwise we check the next battery.
412
++i;
413
}
414
415
// If we fail to enumerate because there are no more batteries to check, then
416
// we can safely say there are indeed no system batteries.
417
return ::GetLastError() != ERROR_NO_MORE_ITEMS;
418
}
419
420
void gfxWindowsPlatform::InitAcceleration() {
421
gfxPlatform::InitAcceleration();
422
423
DeviceManagerDx::Init();
424
425
InitializeConfig();
426
// Ensure devices initialization. SharedSurfaceANGLE and
427
// SharedSurfaceD3D11Interop use them. The devices are lazily initialized
428
// with WebRender to reduce memory usage.
429
// Initialize them now when running non-e10s.
430
if (!BrowserTabsRemoteAutostart()) {
431
EnsureDevicesInitialized();
432
}
433
UpdateANGLEConfig();
434
UpdateRenderMode();
435
436
// If we have Skia and we didn't init dwrite already, do it now.
437
if (!DWriteEnabled() && GetDefaultContentBackend() == BackendType::SKIA) {
438
InitDWriteSupport();
439
}
440
// We need to listen for font setting changes even if DWrite is not used.
441
Factory::SetSystemTextQuality(gfxVars::SystemTextQuality());
442
gfxVars::SetSystemTextQualityListener(
443
gfxDWriteFont::SystemTextQualityChanged);
444
445
// CanUseHardwareVideoDecoding depends on DeviceManagerDx state,
446
// so update the cached value now.
447
UpdateCanUseHardwareVideoDecoding();
448
449
RecordStartupTelemetry();
450
}
451
452
void gfxWindowsPlatform::InitWebRenderConfig() {
453
gfxPlatform::InitWebRenderConfig();
454
455
if (gfxVars::UseWebRender()) {
456
UpdateBackendPrefs();
457
}
458
}
459
460
bool gfxWindowsPlatform::CanUseHardwareVideoDecoding() {
461
DeviceManagerDx* dm = DeviceManagerDx::Get();
462
if (!dm) {
463
return false;
464
}
465
if (!dm->TextureSharingWorks()) {
466
return false;
467
}
468
return !dm->IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
469
}
470
471
bool gfxWindowsPlatform::InitDWriteSupport() {
472
mozilla::ScopedGfxFeatureReporter reporter("DWrite");
473
if (!Factory::EnsureDWriteFactory()) {
474
return false;
475
}
476
477
SetupClearTypeParams();
478
reporter.SetSuccessful();
479
return true;
480
}
481
482
bool gfxWindowsPlatform::HandleDeviceReset() {
483
DeviceResetReason resetReason = DeviceResetReason::OK;
484
if (!DidRenderingDeviceReset(&resetReason)) {
485
return false;
486
}
487
488
if (resetReason != DeviceResetReason::FORCED_RESET) {
489
Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON,
490
uint32_t(resetReason));
491
}
492
493
// Remove devices and adapters.
494
DeviceManagerDx::Get()->ResetDevices();
495
496
imgLoader::NormalLoader()->ClearCache(true);
497
imgLoader::NormalLoader()->ClearCache(false);
498
imgLoader::PrivateBrowsingLoader()->ClearCache(true);
499
imgLoader::PrivateBrowsingLoader()->ClearCache(false);
500
gfxAlphaBoxBlur::ShutdownBlurCache();
501
502
gfxConfig::Reset(Feature::D3D11_COMPOSITING);
503
gfxConfig::Reset(Feature::ADVANCED_LAYERS);
504
gfxConfig::Reset(Feature::D3D11_HW_ANGLE);
505
gfxConfig::Reset(Feature::DIRECT2D);
506
507
InitializeConfig();
508
if (mInitializedDevices) {
509
InitializeDevices();
510
}
511
UpdateANGLEConfig();
512
return true;
513
}
514
515
BackendPrefsData gfxWindowsPlatform::GetBackendPrefs() const {
516
BackendPrefsData data;
517
518
data.mCanvasBitmask = BackendTypeBit(BackendType::SKIA);
519
data.mContentBitmask = BackendTypeBit(BackendType::SKIA);
520
data.mCanvasDefault = BackendType::SKIA;
521
data.mContentDefault = BackendType::SKIA;
522
523
if (gfxConfig::IsEnabled(Feature::DIRECT2D)) {
524
data.mCanvasBitmask |= BackendTypeBit(BackendType::DIRECT2D1_1);
525
data.mCanvasDefault = BackendType::DIRECT2D1_1;
526
// We do not use d2d for content when WebRender is used.
527
if (!gfxVars::UseWebRender()) {
528
data.mContentBitmask |= BackendTypeBit(BackendType::DIRECT2D1_1);
529
data.mContentDefault = BackendType::DIRECT2D1_1;
530
}
531
}
532
return data;
533
}
534
535
void gfxWindowsPlatform::UpdateBackendPrefs() {
536
BackendPrefsData data = GetBackendPrefs();
537
// Remove DIRECT2D1 preference if D2D1Device does not exist.
538
if (!Factory::HasD2D1Device()) {
539
data.mCanvasBitmask &= ~BackendTypeBit(BackendType::DIRECT2D1_1);
540
data.mContentBitmask &= ~BackendTypeBit(BackendType::DIRECT2D1_1);
541
if (data.mCanvasDefault == BackendType::DIRECT2D1_1) {
542
data.mCanvasDefault = BackendType::SKIA;
543
}
544
if (data.mContentDefault == BackendType::DIRECT2D1_1) {
545
data.mContentDefault = BackendType::SKIA;
546
}
547
}
548
InitBackendPrefs(std::move(data));
549
}
550
551
bool gfxWindowsPlatform::IsDirect2DBackend() {
552
return GetDefaultContentBackend() == BackendType::DIRECT2D1_1;
553
}
554
555
void gfxWindowsPlatform::UpdateRenderMode() {
556
bool didReset = HandleDeviceReset();
557
558
UpdateBackendPrefs();
559
560
if (PaintThread::Get()) {
561
PaintThread::Get()->UpdateRenderMode();
562
}
563
564
if (didReset) {
565
mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(
566
IntSize(1, 1), SurfaceFormat::B8G8R8A8);
567
if (!mScreenReferenceDrawTarget) {
568
gfxCriticalNote
569
<< "Failed to update reference draw target after device reset"
570
<< ", D3D11 device:" << hexa(Factory::GetDirect3D11Device().get())
571
<< ", D3D11 status:"
572
<< FeatureStatusToString(
573
gfxConfig::GetValue(Feature::D3D11_COMPOSITING))
574
<< ", D2D1 device:" << hexa(Factory::GetD2D1Device().get())
575
<< ", D2D1 status:"
576
<< FeatureStatusToString(gfxConfig::GetValue(Feature::DIRECT2D))
577
<< ", content:" << int(GetDefaultContentBackend())
578
<< ", compositor:" << int(GetCompositorBackend());
579
MOZ_CRASH(
580
"GFX: Failed to update reference draw target after device reset");
581
}
582
}
583
}
584
585
mozilla::gfx::BackendType gfxWindowsPlatform::GetContentBackendFor(
586
mozilla::layers::LayersBackend aLayers) {
587
mozilla::gfx::BackendType defaultBackend =
588
gfxPlatform::GetDefaultContentBackend();
589
if (aLayers == LayersBackend::LAYERS_D3D11) {
590
return defaultBackend;
591
}
592
593
if (aLayers == LayersBackend::LAYERS_WR &&
594
gfx::gfxVars::UseWebRenderANGLE()) {
595
return defaultBackend;
596
}
597
598
if (defaultBackend == BackendType::DIRECT2D1_1) {
599
// We can't have D2D without D3D11 layers, so fallback to Skia.
600
return BackendType::SKIA;
601
}
602
603
// Otherwise we have some non-accelerated backend and that's ok.
604
return defaultBackend;
605
}
606
607
mozilla::gfx::BackendType gfxWindowsPlatform::GetPreferredCanvasBackend() {
608
mozilla::gfx::BackendType backend = gfxPlatform::GetPreferredCanvasBackend();
609
610
if (backend == BackendType::DIRECT2D1_1 && gfx::gfxVars::UseWebRender() &&
611
!gfx::gfxVars::UseWebRenderANGLE()) {
612
// We can't have D2D without ANGLE when WebRender is enabled, so fallback to
613
// Skia.
614
return BackendType::SKIA;
615
}
616
return backend;
617
}
618
619
gfxPlatformFontList* gfxWindowsPlatform::CreatePlatformFontList() {
620
gfxPlatformFontList* pfl;
621
622
// bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
623
// crashers so blacklist them altogether
624
if (IsNotWin7PreRTM() && DWriteEnabled()) {
625
pfl = new gfxDWriteFontList();
626
if (NS_SUCCEEDED(pfl->InitFontList())) {
627
return pfl;
628
}
629
// DWrite font initialization failed! Don't know why this would happen,
630
// but apparently it can - see bug 594865.
631
// So we're going to fall back to GDI fonts & rendering.
632
gfxPlatformFontList::Shutdown();
633
DisableD2D(FeatureStatus::Failed, "Failed to initialize fonts",
634
NS_LITERAL_CSTRING("FEATURE_FAILURE_FONT_FAIL"));
635
}
636
637
// Ensure this is false, even if the Windows version was recent enough to
638
// permit it, as we're using GDI fonts.
639
mHasVariationFontSupport = false;
640
641
pfl = new gfxGDIFontList();
642
643
if (NS_SUCCEEDED(pfl->InitFontList())) {
644
return pfl;
645
}
646
647
gfxPlatformFontList::Shutdown();
648
return nullptr;
649
}
650
651
// This function will permanently disable D2D for the session. It's intended to
652
// be used when, after initially chosing to use Direct2D, we encounter a
653
// scenario we can't support.
654
//
655
// This is called during gfxPlatform::Init() so at this point there should be no
656
// DrawTargetD2D/1 instances.
657
void gfxWindowsPlatform::DisableD2D(FeatureStatus aStatus, const char* aMessage,
658
const nsACString& aFailureId) {
659
gfxConfig::SetFailed(Feature::DIRECT2D, aStatus, aMessage, aFailureId);
660
Factory::SetDirect3D11Device(nullptr);
661
UpdateBackendPrefs();
662
}
663
664
already_AddRefed<gfxASurface> gfxWindowsPlatform::CreateOffscreenSurface(
665
const IntSize& aSize, gfxImageFormat aFormat) {
666
if (!Factory::AllowedSurfaceSize(aSize)) {
667
return nullptr;
668
}
669
670
RefPtr<gfxASurface> surf = nullptr;
671
672
#ifdef CAIRO_HAS_WIN32_SURFACE
673
if (mRenderMode == RENDER_GDI || mRenderMode == RENDER_DIRECT2D)
674
surf = new gfxWindowsSurface(aSize, aFormat);
675
#endif
676
677
if (!surf || surf->CairoStatus()) {
678
surf = new gfxImageSurface(aSize, aFormat);
679
}
680
681
return surf.forget();
682
}
683
684
static const char kFontAparajita[] = "Aparajita";
685
static const char kFontArabicTypesetting[] = "Arabic Typesetting";
686
static const char kFontArial[] = "Arial";
687
static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
688
static const char kFontCambria[] = "Cambria";
689
static const char kFontCambriaMath[] = "Cambria Math";
690
static const char kFontEbrima[] = "Ebrima";
691
static const char kFontEstrangeloEdessa[] = "Estrangelo Edessa";
692
static const char kFontEuphemia[] = "Euphemia";
693
static const char kFontGabriola[] = "Gabriola";
694
static const char kFontJavaneseText[] = "Javanese Text";
695
static const char kFontKhmerUI[] = "Khmer UI";
696
static const char kFontLaoUI[] = "Lao UI";
697
static const char kFontLeelawadeeUI[] = "Leelawadee UI";
698
static const char kFontLucidaSansUnicode[] = "Lucida Sans Unicode";
699
static const char kFontMVBoli[] = "MV Boli";
700
static const char kFontMalgunGothic[] = "Malgun Gothic";
701
static const char kFontMicrosoftJhengHei[] = "Microsoft JhengHei";
702
static const char kFontMicrosoftNewTaiLue[] = "Microsoft New Tai Lue";
703
static const char kFontMicrosoftPhagsPa[] = "Microsoft PhagsPa";
704
static const char kFontMicrosoftTaiLe[] = "Microsoft Tai Le";
705
static const char kFontMicrosoftUighur[] = "Microsoft Uighur";
706
static const char kFontMicrosoftYaHei[] = "Microsoft YaHei";
707
static const char kFontMicrosoftYiBaiti[] = "Microsoft Yi Baiti";
708
static const char kFontMeiryo[] = "Meiryo";
709
static const char kFontMongolianBaiti[] = "Mongolian Baiti";
710
static const char kFontMyanmarText[] = "Myanmar Text";
711
static const char kFontNirmalaUI[] = "Nirmala UI";
712
static const char kFontNyala[] = "Nyala";
713
static const char kFontPlantagenetCherokee[] = "Plantagenet Cherokee";
714
static const char kFontSegoeUI[] = "Segoe UI";
715
static const char kFontSegoeUIEmoji[] = "Segoe UI Emoji";
716
static const char kFontSegoeUISymbol[] = "Segoe UI Symbol";
717
static const char kFontSylfaen[] = "Sylfaen";
718
static const char kFontTraditionalArabic[] = "Traditional Arabic";
719
static const char kFontTwemojiMozilla[] = "Twemoji Mozilla";
720
static const char kFontUtsaah[] = "Utsaah";
721
static const char kFontYuGothic[] = "Yu Gothic";
722
723
void gfxWindowsPlatform::GetCommonFallbackFonts(
724
uint32_t aCh, uint32_t aNextCh, Script aRunScript,
725
nsTArray<const char*>& aFontList) {
726
EmojiPresentation emoji = GetEmojiPresentation(aCh);
727
if (emoji != EmojiPresentation::TextOnly) {
728
if (aNextCh == kVariationSelector16 ||
729
(aNextCh != kVariationSelector15 &&
730
emoji == EmojiPresentation::EmojiDefault)) {
731
// if char is followed by VS16, try for a color emoji glyph
732
aFontList.AppendElement(kFontSegoeUIEmoji);
733
aFontList.AppendElement(kFontTwemojiMozilla);
734
}
735
}
736
737
// Arial is used as the default fallback for system fallback
738
aFontList.AppendElement(kFontArial);
739
740
if (!IS_IN_BMP(aCh)) {
741
uint32_t p = aCh >> 16;
742
if (p == 1) { // SMP plane
743
aFontList.AppendElement(kFontSegoeUISymbol);
744
aFontList.AppendElement(kFontEbrima);
745
aFontList.AppendElement(kFontNirmalaUI);
746
aFontList.AppendElement(kFontCambriaMath);
747
}
748
} else {
749
uint32_t b = (aCh >> 8) & 0xff;
750
751
switch (b) {
752
case 0x05:
753
aFontList.AppendElement(kFontEstrangeloEdessa);
754
aFontList.AppendElement(kFontCambria);
755
break;
756
case 0x06:
757
aFontList.AppendElement(kFontMicrosoftUighur);
758
break;
759
case 0x07:
760
aFontList.AppendElement(kFontEstrangeloEdessa);
761
aFontList.AppendElement(kFontMVBoli);
762
aFontList.AppendElement(kFontEbrima);
763
break;
764
case 0x09:
765
aFontList.AppendElement(kFontNirmalaUI);
766
aFontList.AppendElement(kFontUtsaah);
767
aFontList.AppendElement(kFontAparajita);
768
break;
769
case 0x0a:
770
case 0x0b:
771
case 0x0c:
772
case 0x0d:
773
aFontList.AppendElement(kFontNirmalaUI);
774
break;
775
case 0x0e:
776
aFontList.AppendElement(kFontLaoUI);
777
aFontList.AppendElement(kFontLeelawadeeUI);
778
break;
779
case 0x10:
780
aFontList.AppendElement(kFontMyanmarText);
781
break;
782
case 0x11:
783
aFontList.AppendElement(kFontMalgunGothic);
784
break;
785
case 0x12:
786
case 0x13:
787
aFontList.AppendElement(kFontNyala);
788
aFontList.AppendElement(kFontPlantagenetCherokee);
789
break;
790
case 0x14:
791
case 0x15:
792
case 0x16:
793
aFontList.AppendElement(kFontEuphemia);
794
aFontList.AppendElement(kFontSegoeUISymbol);
795
break;
796
case 0x17:
797
aFontList.AppendElement(kFontKhmerUI);
798
aFontList.AppendElement(kFontLeelawadeeUI);
799
break;
800
case 0x18: // Mongolian
801
aFontList.AppendElement(kFontMongolianBaiti);
802
aFontList.AppendElement(kFontEuphemia);
803
break;
804
case 0x19:
805
aFontList.AppendElement(kFontMicrosoftTaiLe);
806
aFontList.AppendElement(kFontMicrosoftNewTaiLue);
807
aFontList.AppendElement(kFontKhmerUI);
808
aFontList.AppendElement(kFontLeelawadeeUI);
809
break;
810
case 0x1a:
811
aFontList.AppendElement(kFontLeelawadeeUI);
812
break;
813
case 0x1c:
814
aFontList.AppendElement(kFontNirmalaUI);
815
break;
816
case 0x20: // Symbol ranges
817
case 0x21:
818
case 0x22:
819
case 0x23:
820
case 0x24:
821
case 0x25:
822
case 0x26:
823
case 0x27:
824
case 0x29:
825
case 0x2a:
826
case 0x2b:
827
case 0x2c:
828
aFontList.AppendElement(kFontSegoeUI);
829
aFontList.AppendElement(kFontSegoeUISymbol);
830
aFontList.AppendElement(kFontCambria);
831
aFontList.AppendElement(kFontMeiryo);
832
aFontList.AppendElement(kFontArial);
833
aFontList.AppendElement(kFontLucidaSansUnicode);
834
aFontList.AppendElement(kFontEbrima);
835
break;
836
case 0x2d:
837
case 0x2e:
838
case 0x2f:
839
aFontList.AppendElement(kFontEbrima);
840
aFontList.AppendElement(kFontNyala);
841
aFontList.AppendElement(kFontSegoeUI);
842
aFontList.AppendElement(kFontSegoeUISymbol);
843
aFontList.AppendElement(kFontMeiryo);
844
break;
845
case 0x28: // Braille
846
aFontList.AppendElement(kFontSegoeUISymbol);
847
break;
848
case 0x30:
849
case 0x31:
850
aFontList.AppendElement(kFontMicrosoftYaHei);
851
break;
852
case 0x32:
853
aFontList.AppendElement(kFontMalgunGothic);
854
break;
855
case 0x4d:
856
aFontList.AppendElement(kFontSegoeUISymbol);
857
break;
858
case 0x9f:
859
aFontList.AppendElement(kFontMicrosoftYaHei);
860
aFontList.AppendElement(kFontYuGothic);
861
break;
862
case 0xa0: // Yi
863
case 0xa1:
864
case 0xa2:
865
case 0xa3:
866
case 0xa4:
867
aFontList.AppendElement(kFontMicrosoftYiBaiti);
868
aFontList.AppendElement(kFontSegoeUI);
869
break;
870
case 0xa5:
871
case 0xa6:
872
case 0xa7:
873
aFontList.AppendElement(kFontEbrima);
874
aFontList.AppendElement(kFontSegoeUI);
875
aFontList.AppendElement(kFontCambriaMath);
876
break;
877
case 0xa8:
878
aFontList.AppendElement(kFontMicrosoftPhagsPa);
879
aFontList.AppendElement(kFontNirmalaUI);
880
break;
881
case 0xa9:
882
aFontList.AppendElement(kFontMalgunGothic);
883
aFontList.AppendElement(kFontJavaneseText);
884
aFontList.AppendElement(kFontLeelawadeeUI);
885
break;
886
case 0xaa:
887
aFontList.AppendElement(kFontMyanmarText);
888
break;
889
case 0xab:
890
aFontList.AppendElement(kFontEbrima);
891
aFontList.AppendElement(kFontNyala);
892
break;
893
case 0xd7:
894
aFontList.AppendElement(kFontMalgunGothic);
895
break;
896
case 0xfb:
897
aFontList.AppendElement(kFontMicrosoftUighur);
898
aFontList.AppendElement(kFontGabriola);
899
aFontList.AppendElement(kFontSylfaen);
900
break;
901
case 0xfc:
902
case 0xfd:
903
aFontList.AppendElement(kFontTraditionalArabic);
904
aFontList.AppendElement(kFontArabicTypesetting);
905
break;
906
case 0xfe:
907
aFontList.AppendElement(kFontTraditionalArabic);
908
aFontList.AppendElement(kFontMicrosoftJhengHei);
909
break;
910
case 0xff:
911
aFontList.AppendElement(kFontMicrosoftJhengHei);
912
break;
913
default:
914
break;
915
}
916
}
917
918
// Arial Unicode MS has lots of glyphs for obscure characters,
919
// use it as a last resort
920
aFontList.AppendElement(kFontArialUnicodeMS);
921
}
922
923
gfxFontGroup* gfxWindowsPlatform::CreateFontGroup(
924
const FontFamilyList& aFontFamilyList, const gfxFontStyle* aStyle,
925
gfxTextPerfMetrics* aTextPerf, gfxUserFontSet* aUserFontSet,
926
gfxFloat aDevToCssSize) {
927
return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf, aUserFontSet,
928
aDevToCssSize);
929
}
930
931
bool gfxWindowsPlatform::DidRenderingDeviceReset(
932
DeviceResetReason* aResetReason) {
933
DeviceManagerDx* dm = DeviceManagerDx::Get();
934
if (!dm) {
935
return false;
936
}
937
return dm->HasDeviceReset(aResetReason);
938
}
939
940
void gfxWindowsPlatform::CompositorUpdated() {
941
DeviceManagerDx::Get()->ForceDeviceReset(
942
ForcedDeviceResetReason::COMPOSITOR_UPDATED);
943
UpdateRenderMode();
944
}
945
946
BOOL CALLBACK InvalidateWindowForDeviceReset(HWND aWnd, LPARAM aMsg) {
947
RedrawWindow(aWnd, nullptr, nullptr,
948
RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_FRAME);
949
return TRUE;
950
}
951
952
void gfxWindowsPlatform::SchedulePaintIfDeviceReset() {
953
AUTO_PROFILER_LABEL("gfxWindowsPlatform::SchedulePaintIfDeviceReset", OTHER);
954
955
DeviceResetReason resetReason = DeviceResetReason::OK;
956
if (!DidRenderingDeviceReset(&resetReason)) {
957
return;
958
}
959
960
gfxCriticalNote << "(gfxWindowsPlatform) Detected device reset: "
961
<< (int)resetReason;
962
963
if (XRE_IsParentProcess()) {
964
// Trigger an ::OnPaint for each window.
965
::EnumThreadWindows(GetCurrentThreadId(), InvalidateWindowForDeviceReset,
966
0);
967
} else {
968
NS_DispatchToMainThread(NS_NewRunnableFunction(
969
"gfx::gfxWindowsPlatform::SchedulePaintIfDeviceReset", []() -> void {
970
gfxWindowsPlatform::GetPlatform()->CheckForContentOnlyDeviceReset();
971
}));
972
}
973
974
gfxCriticalNote << "(gfxWindowsPlatform) scheduled device update.";
975
}
976
977
void gfxWindowsPlatform::CheckForContentOnlyDeviceReset() {
978
if (!DidRenderingDeviceReset()) {
979
return;
980
}
981
982
bool isContentOnlyTDR;
983
D3D11DeviceStatus status;
984
985
DeviceManagerDx::Get()->ExportDeviceInfo(&status);
986
CompositorBridgeChild::Get()->SendCheckContentOnlyTDR(status.sequenceNumber(),
987
&isContentOnlyTDR);
988
989
// The parent process doesn't know about the reset yet, or the reset is
990
// local to our device.
991
if (isContentOnlyTDR) {
992
gfxCriticalNote << "A content-only TDR is detected.";
993
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
994
cc->RecvReinitRenderingForDeviceReset();
995
}
996
}
997
998
void gfxWindowsPlatform::GetPlatformCMSOutputProfile(void*& mem,
999
size_t& mem_size) {
1000
WCHAR str[MAX_PATH];
1001
DWORD size = MAX_PATH;
1002
BOOL res;
1003
1004
mem = nullptr;
1005
mem_size = 0;
1006
1007
HDC dc = GetDC(nullptr);
1008
if (!dc) return;
1009
1010
MOZ_SEH_TRY { res = GetICMProfileW(dc, &size, (LPWSTR)&str); }
1011
MOZ_SEH_EXCEPT(GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION) {
1012
res = FALSE;
1013
}
1014
1015
ReleaseDC(nullptr, dc);
1016
if (!res) return;
1017
1018
#ifdef _WIN32
1019
qcms_data_from_unicode_path(str, &mem, &mem_size);
1020
1021
# ifdef DEBUG_tor
1022
if (mem_size > 0)
1023
fprintf(stderr, "ICM profile read from %s successfully\n",
1024
NS_ConvertUTF16toUTF8(str).get());
1025
# endif // DEBUG_tor
1026
#endif // _WIN32
1027
}
1028
1029
void gfxWindowsPlatform::GetDLLVersion(char16ptr_t aDLLPath,
1030
nsAString& aVersion) {
1031
DWORD versInfoSize, vers[4] = {0};
1032
// version info not available case
1033
aVersion.AssignLiteral(u"0.0.0.0");
1034
versInfoSize = GetFileVersionInfoSizeW(aDLLPath, nullptr);
1035
AutoTArray<BYTE, 512> versionInfo;
1036
1037
if (versInfoSize == 0 ||
1038
!versionInfo.AppendElements(uint32_t(versInfoSize))) {
1039
return;
1040
}
1041
1042
if (!GetFileVersionInfoW(aDLLPath, 0, versInfoSize,
1043
LPBYTE(versionInfo.Elements()))) {
1044
return;
1045
}
1046
1047
UINT len = 0;
1048
VS_FIXEDFILEINFO* fileInfo = nullptr;
1049
if (!VerQueryValue(LPBYTE(versionInfo.Elements()), TEXT("\\"),
1050
(LPVOID*)&fileInfo, &len) ||
1051
len == 0 || fileInfo == nullptr) {
1052
return;
1053
}
1054
1055
DWORD fileVersMS = fileInfo->dwFileVersionMS;
1056
DWORD fileVersLS = fileInfo->dwFileVersionLS;
1057
1058
vers[0] = HIWORD(fileVersMS);
1059
vers[1] = LOWORD(fileVersMS);
1060
vers[2] = HIWORD(fileVersLS);
1061
vers[3] = LOWORD(fileVersLS);
1062
1063
char buf[256];
1064
SprintfLiteral(buf, "%u.%u.%u.%u", vers[0], vers[1], vers[2], vers[3]);
1065
aVersion.Assign(NS_ConvertUTF8toUTF16(buf));
1066
}
1067
1068
static BOOL CALLBACK AppendClearTypeParams(HMONITOR aMonitor, HDC, LPRECT,
1069
LPARAM aContext) {
1070
MONITORINFOEXW monitorInfo;
1071
monitorInfo.cbSize = sizeof(MONITORINFOEXW);
1072
if (!GetMonitorInfoW(aMonitor, &monitorInfo)) {
1073
return TRUE;
1074
}
1075
1076
ClearTypeParameterInfo ctinfo;
1077
ctinfo.displayName.Assign(monitorInfo.szDevice);
1078
1079
RefPtr<IDWriteRenderingParams> renderingParams;
1080
HRESULT hr = Factory::GetDWriteFactory()->CreateMonitorRenderingParams(
1081
aMonitor, getter_AddRefs(renderingParams));
1082
if (FAILED(hr)) {
1083
return TRUE;
1084
}
1085
1086
ctinfo.gamma = renderingParams->GetGamma() * 1000;
1087
ctinfo.pixelStructure = renderingParams->GetPixelGeometry();
1088
ctinfo.clearTypeLevel = renderingParams->GetClearTypeLevel() * 100;
1089
ctinfo.enhancedContrast = renderingParams->GetEnhancedContrast() * 100;
1090
1091
auto* params = reinterpret_cast<nsTArray<ClearTypeParameterInfo>*>(aContext);
1092
params->AppendElement(ctinfo);
1093
return TRUE;
1094
}
1095
1096
void gfxWindowsPlatform::GetCleartypeParams(
1097
nsTArray<ClearTypeParameterInfo>& aParams) {
1098
aParams.Clear();
1099
if (!DWriteEnabled()) {
1100
return;
1101
}
1102
EnumDisplayMonitors(nullptr, nullptr, AppendClearTypeParams,
1103
reinterpret_cast<LPARAM>(&aParams));
1104
}
1105
1106
void gfxWindowsPlatform::FontsPrefsChanged(const char* aPref) {
1107
bool clearTextFontCaches = true;
1108
1109
gfxPlatform::FontsPrefsChanged(aPref);
1110
1111
if (aPref &&
1112
!strncmp(GFX_CLEARTYPE_PARAMS, aPref, strlen(GFX_CLEARTYPE_PARAMS))) {
1113
SetupClearTypeParams();
1114
} else {
1115
clearTextFontCaches = false;
1116
}
1117
1118
if (clearTextFontCaches) {
1119
gfxFontCache* fc = gfxFontCache::GetCache();
1120
if (fc) {
1121
fc->Flush();
1122
}
1123
}
1124
}
1125
1126
#define DISPLAY1_REGISTRY_KEY \
1127
HKEY_CURRENT_USER, L"Software\\Microsoft\\Avalon.Graphics\\DISPLAY1"
1128
1129
#define ENHANCED_CONTRAST_VALUE_NAME L"EnhancedContrastLevel"
1130
1131
void gfxWindowsPlatform::SetupClearTypeParams() {
1132
if (DWriteEnabled()) {
1133
// any missing prefs will default to invalid (-1) and be ignored;
1134
// out-of-range values will also be ignored
1135
FLOAT gamma = -1.0;
1136
FLOAT contrast = -1.0;
1137
FLOAT level = -1.0;
1138
int geometry = -1;
1139
int mode = -1;
1140
int32_t value;
1141
if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_GAMMA, &value))) {
1142
if (value >= 1000 && value <= 2200) {
1143
gamma = FLOAT(value / 1000.0);
1144
}
1145
}
1146
1147
if (NS_SUCCEEDED(
1148
Preferences::GetInt(GFX_CLEARTYPE_PARAMS_CONTRAST, &value))) {
1149
if (value >= 0 && value <= 1000) {
1150
contrast = FLOAT(value / 100.0);
1151
}
1152
}
1153
1154
if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_LEVEL, &value))) {
1155
if (value >= 0 && value <= 100) {
1156
level = FLOAT(value / 100.0);
1157
}
1158
}
1159
1160
if (NS_SUCCEEDED(
1161
Preferences::GetInt(GFX_CLEARTYPE_PARAMS_STRUCTURE, &value))) {
1162
if (value >= 0 && value <= 2) {
1163
geometry = value;
1164
}
1165
}
1166
1167
if (NS_SUCCEEDED(Preferences::GetInt(GFX_CLEARTYPE_PARAMS_MODE, &value))) {
1168
if (value >= 0 && value <= 5) {
1169
mode = value;
1170
}
1171
}
1172
1173
cairo_dwrite_set_cleartype_params(gamma, contrast, level, geometry, mode);
1174
1175
switch (mode) {
1176
case DWRITE_RENDERING_MODE_ALIASED:
1177
case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:
1178
mMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
1179
break;
1180
case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:
1181
mMeasuringMode = DWRITE_MEASURING_MODE_GDI_NATURAL;
1182
break;
1183
default:
1184
mMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
1185
break;
1186
}
1187
1188
RefPtr<IDWriteRenderingParams> defaultRenderingParams;
1189
Factory::GetDWriteFactory()->CreateRenderingParams(
1190
getter_AddRefs(defaultRenderingParams));
1191
// For EnhancedContrast, we override the default if the user has not set it
1192
// in the registry (by using the ClearType Tuner).
1193
if (contrast < 0.0 || contrast > 10.0) {
1194
HKEY hKey;
1195
LONG res = RegOpenKeyExW(DISPLAY1_REGISTRY_KEY, 0, KEY_READ, &hKey);
1196
if (res == ERROR_SUCCESS) {
1197
res = RegQueryValueExW(hKey, ENHANCED_CONTRAST_VALUE_NAME, nullptr,
1198
nullptr, nullptr, nullptr);
1199
if (res == ERROR_SUCCESS) {
1200
contrast = defaultRenderingParams->GetEnhancedContrast();
1201
}
1202
RegCloseKey(hKey);
1203
}
1204
1205
if (contrast < 0.0 || contrast > 10.0) {
1206
contrast = 1.0;
1207
}
1208
}
1209
1210
// For parameters that have not been explicitly set,
1211
// we copy values from default params (or our overridden value for contrast)
1212
if (gamma < 1.0 || gamma > 2.2) {
1213
gamma = defaultRenderingParams->GetGamma();
1214
}
1215
1216
if (level < 0.0 || level > 1.0) {
1217
level = defaultRenderingParams->GetClearTypeLevel();
1218
}
1219
1220
DWRITE_PIXEL_GEOMETRY dwriteGeometry =
1221
static_cast<DWRITE_PIXEL_GEOMETRY>(geometry);
1222
DWRITE_RENDERING_MODE renderMode = static_cast<DWRITE_RENDERING_MODE>(mode);
1223
1224
if (dwriteGeometry < DWRITE_PIXEL_GEOMETRY_FLAT ||
1225
dwriteGeometry > DWRITE_PIXEL_GEOMETRY_BGR) {
1226
dwriteGeometry = defaultRenderingParams->GetPixelGeometry();
1227
}
1228
1229
Factory::SetBGRSubpixelOrder(dwriteGeometry == DWRITE_PIXEL_GEOMETRY_BGR);
1230
1231
if (renderMode < DWRITE_RENDERING_MODE_DEFAULT ||
1232
renderMode > DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC) {
1233
renderMode = defaultRenderingParams->GetRenderingMode();
1234
}
1235
1236
mRenderingParams[TEXT_RENDERING_NO_CLEARTYPE] = defaultRenderingParams;
1237
1238
HRESULT hr = Factory::GetDWriteFactory()->CreateCustomRenderingParams(
1239
gamma, contrast, level, dwriteGeometry, renderMode,
1240
getter_AddRefs(mRenderingParams[TEXT_RENDERING_NORMAL]));
1241
if (FAILED(hr) || !mRenderingParams[TEXT_RENDERING_NORMAL]) {
1242
mRenderingParams[TEXT_RENDERING_NORMAL] = defaultRenderingParams;
1243
}
1244
1245
hr = Factory::GetDWriteFactory()->CreateCustomRenderingParams(
1246
gamma, contrast, level, dwriteGeometry,
1247
DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC,
1248
getter_AddRefs(mRenderingParams[TEXT_RENDERING_GDI_CLASSIC]));
1249
if (FAILED(hr) || !mRenderingParams[TEXT_RENDERING_GDI_CLASSIC]) {
1250
mRenderingParams[TEXT_RENDERING_GDI_CLASSIC] = defaultRenderingParams;
1251
}
1252
}
1253
}
1254
1255
ReadbackManagerD3D11* gfxWindowsPlatform::GetReadbackManager() {
1256
if (!mD3D11ReadbackManager) {
1257
mD3D11ReadbackManager = new ReadbackManagerD3D11();
1258
}
1259
1260
return mD3D11ReadbackManager;
1261
}
1262
1263
bool gfxWindowsPlatform::IsOptimus() {
1264
static int knowIsOptimus = -1;
1265
if (knowIsOptimus == -1) {
1266
// other potential optimus -- nvd3d9wrapx.dll & nvdxgiwrap.dll
1267
if (GetModuleHandleA("nvumdshim.dll") ||
1268
GetModuleHandleA("nvumdshimx.dll")) {
1269
knowIsOptimus = 1;
1270
} else {
1271
knowIsOptimus = 0;
1272
}
1273
}
1274
return knowIsOptimus;
1275
}
1276
/*
1277
static inline bool
1278
IsWARPStable()
1279
{
1280
// It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703.
1281
if (!IsWin8OrLater() || GetModuleHandleA("nvdxgiwrap.dll")) {
1282
return false;
1283
}
1284
return true;
1285
}
1286
*/
1287
static void InitializeANGLEConfig() {
1288
FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
1289
1290
if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1291
d3d11ANGLE.DisableByDefault(
1292
FeatureStatus::Unavailable, "D3D11 compositing is disabled",
1293
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_DISABLED"));
1294
return;
1295
}
1296
1297
d3d11ANGLE.EnableByDefault();
1298
1299
nsCString message;
1300
nsCString failureId;
1301
if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
1302
&message, failureId)) {
1303
d3d11ANGLE.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
1304
}
1305
}
1306
1307
void gfxWindowsPlatform::InitializeDirectDrawConfig() {
1308
MOZ_ASSERT(XRE_IsParentProcess());
1309
1310
FeatureState& ddraw = gfxConfig::GetFeature(Feature::DIRECT_DRAW);
1311
ddraw.EnableByDefault();
1312
}
1313
1314
void gfxWindowsPlatform::InitializeConfig() {
1315
if (XRE_IsParentProcess()) {
1316
// The parent process first determines which features can be attempted.
1317
// This information is relayed to content processes and the GPU process.
1318
InitializeD3D11Config();
1319
InitializeANGLEConfig();
1320
InitializeD2DConfig();
1321
} else {
1322
FetchAndImportContentDeviceData();
1323
InitializeANGLEConfig();
1324
}
1325
}
1326
1327
void gfxWindowsPlatform::InitializeD3D11Config() {
1328
MOZ_ASSERT(XRE_IsParentProcess());
1329
1330
FeatureState& d3d11 = gfxConfig::GetFeature(Feature::D3D11_COMPOSITING);
1331
1332
if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
1333
d3d11.DisableByDefault(
1334
FeatureStatus::Unavailable, "Hardware compositing is disabled",
1335
NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_NEED_HWCOMP"));
1336
return;
1337
}
1338
1339
d3d11.EnableByDefault();
1340
1341
// Check if the user really, really wants WARP.
1342
if (StaticPrefs::layers_d3d11_force_warp_AtStartup()) {
1343
// Force D3D11 on even if we disabled it.
1344
d3d11.UserForceEnable("User force-enabled WARP");
1345
}
1346
1347
if (!IsWin8OrLater() &&
1348
!DeviceManagerDx::Get()->CheckRemotePresentSupport()) {
1349
nsCOMPtr<nsIGfxInfo> gfxInfo;
1350
gfxInfo = services::GetGfxInfo();
1351
nsAutoString adaptorId;
1352
gfxInfo->GetAdapterDeviceID(adaptorId);
1353
// Blacklist Intel HD Graphics 510/520/530 on Windows 7 without platform
1354
// update due to the crashes in Bug 1351349.
1355
if (adaptorId.EqualsLiteral("0x1912") ||
1356
adaptorId.EqualsLiteral("0x1916") ||
1357
adaptorId.EqualsLiteral("0x1902")) {
1358
#ifdef RELEASE_OR_BETA
1359
d3d11.Disable(FeatureStatus::Blacklisted, "Blacklisted, see bug 1351349",
1360
NS_LITERAL_CSTRING("FEATURE_FAILURE_BUG_1351349"));
1361
#else
1362
Preferences::SetBool("gfx.compositor.clearstate", true);
1363
#endif
1364
}
1365
}
1366
1367
nsCString message;
1368
nsCString failureId;
1369
if (StaticPrefs::layers_d3d11_enable_blacklist_AtStartup() &&
1370
!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
1371
&message, failureId)) {
1372
d3d11.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
1373
}
1374
1375
InitializeAdvancedLayersConfig();
1376
}
1377
1378
/* static */
1379
void gfxWindowsPlatform::InitializeAdvancedLayersConfig() {
1380
// Only enable Advanced Layers if D3D11 succeeded.
1381
if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1382
return;
1383
}
1384
1385
FeatureState& al = gfxConfig::GetFeature(Feature::ADVANCED_LAYERS);
1386
al.SetDefaultFromPref(StaticPrefs::GetPrefName_layers_mlgpu_enabled(),
1387
true /* aIsEnablePref */,
1388
StaticPrefs::GetPrefDefault_layers_mlgpu_enabled());
1389
1390
// Windows 7 has an extra pref since it uses totally different buffer paths
1391
// that haven't been performance tested yet.
1392
if (al.IsEnabled() && !IsWin8OrLater()) {
1393
if (StaticPrefs::layers_mlgpu_enable_on_windows7_AtStartup()) {
1394
al.UserEnable("Enabled for Windows 7 via user-preference");
1395
} else {
1396
al.Disable(FeatureStatus::Disabled,
1397
"Advanced Layers is disabled on Windows 7 by default",
1398
NS_LITERAL_CSTRING("FEATURE_FAILURE_DISABLED_ON_WIN7"));
1399
}
1400
}
1401
1402
nsCString message, failureId;
1403
if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_ADVANCED_LAYERS, &message,
1404
failureId)) {
1405
al.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
1406
} else if (Preferences::GetBool("layers.mlgpu.sanity-test-failed", false)) {
1407
al.Disable(FeatureStatus::Broken, "Failed to render sanity test",
1408
NS_LITERAL_CSTRING("FEATURE_FAILURE_FAILED_TO_RENDER"));
1409
}
1410
}
1411
1412
/* static */
1413
void gfxWindowsPlatform::RecordContentDeviceFailure(
1414
TelemetryDeviceCode aDevice) {
1415
// If the parent process fails to acquire a device, we record this
1416
// normally as part of the environment. The exceptional case we're
1417
// looking for here is when the parent process successfully acquires
1418
// a device, but the content process fails to acquire the same device.
1419
// This would not normally be displayed in about:support.
1420
if (!XRE_IsContentProcess()) {
1421
return;
1422
}
1423
Telemetry::Accumulate(Telemetry::GFX_CONTENT_FAILED_TO_ACQUIRE_DEVICE,
1424
uint32_t(aDevice));
1425
}
1426
1427
void gfxWindowsPlatform::RecordStartupTelemetry() {
1428
if (!XRE_IsParentProcess()) {
1429
return;
1430
}
1431
1432
DeviceManagerDx* dx = DeviceManagerDx::Get();
1433
nsTArray<DXGI_OUTPUT_DESC1> outputs = dx->EnumerateOutputs();
1434
1435
uint32_t allSupportedColorSpaces = 0;
1436
for (auto& output : outputs) {
1437
uint32_t colorSpace = 1 << output.ColorSpace;
1438
allSupportedColorSpaces |= colorSpace;
1439
}
1440
1441
Telemetry::ScalarSet(
1442
Telemetry::ScalarID::GFX_HDR_WINDOWS_DISPLAY_COLORSPACE_BITFIELD,
1443
allSupportedColorSpaces);
1444
}
1445
1446
// Supports lazy device initialization on Windows, so that WebRender can avoid
1447
// initializing GPU state and allocating swap chains for most non-GPU processes.
1448
void gfxWindowsPlatform::EnsureDevicesInitialized() {
1449
if (!mInitializedDevices) {
1450
mInitializedDevices = true;
1451
InitializeDevices();
1452
UpdateBackendPrefs();
1453
}
1454
}
1455
1456
bool gfxWindowsPlatform::DevicesInitialized() { return mInitializedDevices; }
1457
1458
void gfxWindowsPlatform::InitializeDevices() {
1459
MOZ_ASSERT(NS_IsMainThread());
1460
1461
if (XRE_IsParentProcess()) {
1462
// If we're the UI process, and the GPU process is enabled, then we don't
1463
// initialize any DirectX devices. We do leave them enabled in gfxConfig
1464
// though. If the GPU process fails to create these devices it will send
1465
// a message back and we'll update their status.
1466
if (InitGPUProcessSupport()) {
1467
return;
1468
}
1469
1470
// No GPU process, continue initializing devices as normal.
1471
}
1472
1473
// If acceleration is disabled, we refuse to initialize anything.
1474
if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
1475
return;
1476
}
1477
1478
// If we previously crashed initializing devices, bail out now.
1479
D3D11LayersCrashGuard detectCrashes;
1480
if (detectCrashes.Crashed()) {
1481
gfxConfig::SetFailed(Feature::HW_COMPOSITING,
1482
FeatureStatus::CrashedOnStartup,
1483
"Crashed during startup in a previous session");
1484
gfxConfig::SetFailed(
1485
Feature::D3D11_COMPOSITING, FeatureStatus::CrashedOnStartup,
1486
"Harware acceleration crashed during startup in a previous session");
1487
gfxConfig::SetFailed(
1488
Feature::DIRECT2D, FeatureStatus::CrashedOnStartup,
1489
"Harware acceleration crashed during startup in a previous session");
1490
return;
1491
}
1492
1493
bool shouldUseD2D = gfxConfig::IsEnabled(Feature::DIRECT2D);
1494
1495
// First, initialize D3D11. If this succeeds we attempt to use Direct2D.
1496
InitializeD3D11();
1497
InitializeD2D();
1498
1499
if (!gfxConfig::IsEnabled(Feature::DIRECT2D) && XRE_IsContentProcess() &&
1500
shouldUseD2D) {
1501
RecordContentDeviceFailure(TelemetryDeviceCode::D2D1);
1502
}
1503
}
1504
1505
void gfxWindowsPlatform::InitializeD3D11() {
1506
// This function attempts to initialize our D3D11 devices, if the hardware
1507
// is not blacklisted for D3D11 layers. This first attempt will try to create
1508
// a hardware accelerated device. If this creation fails or the hardware is
1509
// blacklisted, then this function will abort if WARP is disabled, causing us
1510
// to fallback to Basic layers. If WARP is not disabled it will use a WARP
1511
// device which should always be available on Windows 7 and higher.
1512
if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1513
return;
1514
}
1515
1516
DeviceManagerDx* dm = DeviceManagerDx::Get();
1517
if (XRE_IsParentProcess()) {
1518
if (!dm->CreateCompositorDevices()) {
1519
return;
1520
}
1521
}
1522
1523
dm->CreateContentDevices();
1524
1525
// Content process failed to create the d3d11 device while parent process
1526
// succeed.
1527
if (XRE_IsContentProcess() &&
1528
!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1529
gfxCriticalError()
1530
<< "[D3D11] Failed to create the D3D11 device in content \
1531
process.";
1532
}
1533
}
1534
1535
void gfxWindowsPlatform::InitializeD2DConfig() {
1536
FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
1537
1538
if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1539
d2d1.DisableByDefault(FeatureStatus::Unavailable,
1540
"Direct2D requires Direct3D 11 compositing",
1541
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_D3D11_COMP"));
1542
return;
1543
}
1544
1545
d2d1.SetDefaultFromPref(StaticPrefs::GetPrefName_gfx_direct2d_disabled(),
1546
false,
1547
StaticPrefs::GetPrefDefault_gfx_direct2d_disabled());
1548
1549
nsCString message;
1550
nsCString failureId;
1551
if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT2D, &message,
1552
failureId)) {
1553
d2d1.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
1554
}
1555
1556
if (!d2d1.IsEnabled() &&
1557
StaticPrefs::gfx_direct2d_force_enabled_AtStartup()) {
1558
d2d1.UserForceEnable("Force-enabled via user-preference");
1559
}
1560
}
1561
1562
void gfxWindowsPlatform::InitializeD2D() {
1563
ScopedGfxFeatureReporter d2d1_1("D2D1.1");
1564
1565
FeatureState& d2d1 = gfxConfig::GetFeature(Feature::DIRECT2D);
1566
1567
DeviceManagerDx* dm = DeviceManagerDx::Get();
1568
1569
// We don't know this value ahead of time, but the user can force-override
1570
// it, so we use Disable instead of SetFailed.
1571
if (dm->IsWARP()) {
1572
d2d1.Disable(FeatureStatus::Blocked,
1573
"Direct2D is not compatible with Direct3D11 WARP",
1574
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_WARP_BLOCK"));
1575
}
1576
1577
// If we pass all the initial checks, we can proceed to runtime decisions.
1578
if (!d2d1.IsEnabled()) {
1579
return;
1580
}
1581
1582
if (!Factory::SupportsD2D1()) {
1583
d2d1.SetFailed(FeatureStatus::Unavailable,
1584
"Failed to acquire a Direct2D 1.1 factory",
1585
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_FACTORY"));
1586
return;
1587
}
1588
1589
if (!dm->GetContentDevice()) {
1590
d2d1.SetFailed(FeatureStatus::Failed,
1591
"Failed to acquire a Direct3D 11 content device",
1592
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_DEVICE"));
1593
return;
1594
}
1595
1596
if (!dm->TextureSharingWorks()) {
1597
d2d1.SetFailed(FeatureStatus::Failed,
1598
"Direct3D11 device does not support texture sharing",
1599
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_TXT_SHARING"));
1600
return;
1601
}
1602
1603
// Using Direct2D depends on DWrite support.
1604
if (!DWriteEnabled() && !InitDWriteSupport()) {
1605
d2d1.SetFailed(FeatureStatus::Failed,
1606
"Failed to initialize DirectWrite support",
1607
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_DWRITE"));
1608
return;
1609
}
1610
1611
// Verify that Direct2D device creation succeeded.
1612
RefPtr<ID3D11Device> contentDevice = dm->GetContentDevice();
1613
if (!Factory::SetDirect3D11Device(contentDevice)) {
1614
d2d1.SetFailed(FeatureStatus::Failed, "Failed to create a Direct2D device",
1615
NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_CREATE_FAILED"));
1616
return;
1617
}
1618
1619
MOZ_ASSERT(d2d1.IsEnabled());
1620
d2d1_1.SetSuccessful();
1621
}
1622
1623
bool gfxWindowsPlatform::InitGPUProcessSupport() {
1624
FeatureState& gpuProc = gfxConfig::GetFeature(Feature::GPU_PROCESS);
1625
1626
if (!gpuProc.IsEnabled()) {
1627
return false;
1628
}
1629
1630
nsCString message;
1631
nsCString failureId;
1632
if (!gfxPlatform::IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_GPU_PROCESS,
1633
&message, failureId)) {
1634
gpuProc.Disable(FeatureStatus::Blacklisted, message.get(), failureId);
1635
return false;
1636
}
1637
1638
if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
1639
// Don't use the GPU process if not using D3D11, unless software
1640
// compositor is allowed
1641
if (StaticPrefs::layers_gpu_process_allow_software_AtStartup()) {
1642
return gpuProc.IsEnabled();
1643
}
1644
gpuProc.Disable(FeatureStatus::Unavailable,
1645
"Not using GPU Process since D3D11 is unavailable",
1646
NS_LITERAL_CSTRING("FEATURE_FAILURE_NO_D3D11"));
1647
} else if (!IsWin7SP1OrLater()) {
1648
// On Windows 7 Pre-SP1, DXGI 1.2 is not available and remote presentation
1649
// for D3D11 will not work. Rather than take a regression we revert back
1650
// to in-process rendering.
1651
gpuProc.Disable(FeatureStatus::Unavailable,
1652
"Windows 7 Pre-SP1 cannot use the GPU process",
1653
NS_LITERAL_CSTRING("FEATURE_FAILURE_OLD_WINDOWS"));
1654
} else if (!IsWin8OrLater()) {
1655
// Windows 7 SP1 can have DXGI 1.2 only via the Platform Update, so we
1656
// explicitly check for that here.
1657
if (!DeviceManagerDx::Get()->CheckRemotePresentSupport()) {
1658
gpuProc.Disable(FeatureStatus::Unavailable,
1659
"GPU Process requires the Windows 7 Platform Update",
1660
NS_LITERAL_CSTRING("FEATURE_FAILURE_PLATFORM_UPDATE"));
1661
} else {
1662
// Clear anything cached by the above call since we don't need it.
1663
DeviceManagerDx::Get()->ResetDevices();
1664
}
1665
}
1666
1667
// If we're still enabled at this point, the user set the force-enabled pref.
1668
return gpuProc.IsEnabled();
1669
}
1670
1671
bool gfxWindowsPlatform::DwmCompositionEnabled() {
1672
BOOL dwmEnabled = false;
1673
1674
if (FAILED(DwmIsCompositionEnabled(&dwmEnabled))) {
1675
return false;
1676
}
1677
1678
return dwmEnabled;
1679
}
1680
1681
class D3DVsyncSource final : public VsyncSource {
1682
public:
1683
class D3DVsyncDisplay final : public VsyncSource::Display {
1684
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(D3DVsyncDisplay)
1685
public:
1686
D3DVsyncDisplay()
1687
: mPrevVsync(TimeStamp::Now()),
1688
mVsyncEnabledLock("D3DVsyncEnabledLock"),
1689
mVsyncEnabled(false) {
1690
mVsyncThread = new base::Thread("WindowsVsyncThread");
1691
MOZ_RELEASE_ASSERT(mVsyncThread->Start(),
1692
"GFX: Could not start Windows vsync thread");
1693
SetVsyncRate();
1694
}
1695
1696
void SetVsyncRate() {
1697
if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
1698
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
1699
return;
1700
}
1701
1702
DWM_TIMING_INFO vblankTime;
1703
// Make sure to init the cbSize, otherwise GetCompositionTiming will fail
1704
vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
1705
HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
1706
if (SUCCEEDED(hr)) {
1707
UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
1708
// We get the rate in hertz / time, but we want the rate in ms.
1709
float rate = ((float)refreshRate.uiDenominator /
1710
(float)refreshRate.uiNumerator) *
1711
1000;
1712
mVsyncRate = TimeDuration::FromMilliseconds(rate);
1713
} else {
1714
mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
1715
}
1716
}
1717
1718
virtual void Shutdown() override {
1719
MOZ_ASSERT(NS_IsMainThread());
1720
DisableVsync();
1721
mVsyncThread->Stop();
1722
delete mVsyncThread;
1723
}
1724
1725
virtual void EnableVsync() override {
1726
MOZ_ASSERT(NS_IsMainThread());
1727
MOZ_ASSERT(mVsyncThread->IsRunning());
1728
{ // scope lock
1729
MonitorAutoLock lock(mVsyncEnabledLock);
1730
if (mVsyncEnabled) {
1731
return;
1732
}
1733
mVsyncEnabled = true;
1734
}
1735
1736
mVsyncThread->message_loop()->PostTask(NewRunnableMethod(
1737
"D3DVsyncDisplay::VBlankLoop", this, &D3DVsyncDisplay::VBlankLoop));
1738
}
1739
1740
virtual void DisableVsync() override {
1741
MOZ_ASSERT(NS_IsMainThread());
1742
MOZ_ASSERT(mVsyncThread->IsRunning());
1743
MonitorAutoLock lock(mVsyncEnabledLock);
1744
if (!mVsyncEnabled) {
1745
return;
1746
}
1747
mVsyncEnabled = false;
1748
}
1749
1750
virtual bool IsVsyncEnabled() override {
1751
MOZ_ASSERT(NS_IsMainThread());
1752
MonitorAutoLock lock(mVsyncEnabledLock);
1753
return mVsyncEnabled;
1754
}
1755
1756
virtual TimeDuration GetVsyncRate() override { return mVsyncRate; }
1757
1758
void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp) {
1759
MOZ_ASSERT(IsInVsyncThread());
1760
NS_WARNING(
1761
"DwmComposition dynamically disabled, falling back to software "
1762
"timers");
1763
1764
TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
1765
TimeDuration delay = nextVsync - TimeStamp::Now();
1766
if (delay.ToMilliseconds() < 0) {
1767
delay = mozilla::TimeDuration::FromMilliseconds(0);
1768
}
1769
1770
mVsyncThread->message_loop()->PostDelayedTask(
1771
NewRunnableMethod("D3DVsyncDisplay::VBlankLoop", this,
1772
&D3DVsyncDisplay::VBlankLoop),
1773
delay.ToMilliseconds());
1774
}
1775
1776
// Returns the timestamp for the just happened vsync
1777
TimeStamp GetVBlankTime() {
1778
TimeStamp vsync = TimeStamp::Now();
1779
TimeStamp now = vsync;
1780
1781
DWM_TIMING_INFO vblankTime;
1782
// Make sure to init the cbSize, otherwise
1783
// GetCompositionTiming will fail
1784
vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
1785
HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
1786
if (!SUCCEEDED(hr)) {
1787
return vsync;
1788
}
1789
1790
LARGE_INTEGER frequency;
1791
QueryPerformanceFrequency(&frequency);
1792
1793
LARGE_INTEGER qpcNow;
1794
QueryPerformanceCounter(&qpcNow);
1795
1796
const int microseconds = 1000000;
1797
int64_t adjust = qpcNow.QuadPart - vblankTime.qpcVBlank;
1798
int64_t usAdjust = (adjust * microseconds) / frequency.QuadPart;
1799
vsync -= TimeDuration::FromMicroseconds((double)usAdjust);
1800
1801
if (IsWin10OrLater()) {
1802
// On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
1803
// reports the upcoming vsync time, which is in the future.
1804
// It can also sometimes report a vblank time in the past.
1805
// Since large parts of Gecko assume TimeStamps can't be in future,
1806
// use the previous vsync.
1807
1808
// Windows 10 and Intel HD vsync timestamps are messy and
1809
// all over the place once in a while. Most of the time,
1810
// it reports the upcoming vsync. Sometimes, that upcoming
1811
// vsync is in the past. Sometimes that upcoming vsync is before
1812
// the previously seen vsync.
1813
// In these error cases, normalize to Now();
1814
if (vsync >= now) {
1815
vsync = vsync - mVsyncRate;
1816
}
1817
}
1818
1819
// On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
1820
// from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
1821
if (vsync >= now) {
1822
vsync = now;
1823
}
1824
1825
// Our vsync time is some time very far in the past, adjust to Now.
1826
// 4 ms is arbitrary, so feel free to pick something else if this isn't
1827
// working. See the comment above within IsWin10OrLater().
1828
if ((now - vsync).ToMilliseconds() > 4.0) {
1829
vsync = now;
1830
}
1831
1832
return vsync;
1833
}
1834
1835
void VBlankLoop() {
1836
MOZ_ASSERT(IsInVsyncThread());
1837
MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));
1838
1839
TimeStamp vsync = TimeStamp::Now();
1840
mPrevVsync = TimeStamp();
1841
TimeStamp flushTime = TimeStamp::Now();
1842
TimeDuration longVBlank = mVsyncRate * 2;
1843
1844
for (;;) {
1845
{ // scope lock
1846
MonitorAutoLock lock(mVsyncEnabledLock);
1847
if (!mVsyncEnabled) return;
1848
}
1849
1850
// Large parts of gecko assume that the refresh driver timestamp
1851
// must be <= Now() and cannot be in the future.
1852
MOZ_ASSERT(vsync <= TimeStamp::Now());
1853
Display::NotifyVsync(vsync);
1854
1855
// DwmComposition can be dynamically enabled/disabled
1856
// so we have to check every time that it's available.
1857
// When it is unavailable, we fallback to software but will try
1858
// to get back to dwm rendering once it's re-enabled
1859
if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
1860
ScheduleSoftwareVsync(vsync);
1861
return;
1862
}
1863
1864
// Using WaitForVBlank, the whole system dies because WaitForVBlank
1865
// only works if it's run on the same thread as the Present();
1866
HRESULT hr = DwmFlush();
1867
if (!SUCCEEDED(hr)) {
1868
// DWMFlush isn't working, fallback to software vsync.
1869
ScheduleSoftwareVsync(TimeStamp::Now());
1870
return;
1871
}
1872
1873
TimeStamp now = TimeStamp::Now();
1874
TimeDuration flushDiff = now - flushTime;
1875
flushTime = now;
1876
if ((flushDiff > longVBlank) || mPrevVsync.IsNull()) {
1877
// Our vblank took longer than 2 intervals, readjust our timestamps
1878
vsync = GetVBlankTime();
1879
mPrevVsync = vsync;
1880
} else {
1881
// Instead of giving the actual vsync time, a constant interval
1882
// between vblanks instead of the noise generated via hardware
1883
// is actually what we want. Most apps just care about the diff
1884
// between vblanks to animate, so a clean constant interval is
1885
// smoother.
1886
vsync = mPrevVsync + mVsyncRate;
1887
if (vsync > now) {
1888
// DWMFlush woke up very early, so readjust our times again
1889
vsync = GetVBlankTime();
1890
}
1891
1892
if (vsync <= mPrevVsync) {
1893
vsync = TimeStamp::Now();
1894
}
1895
1896
if ((now - vsync).ToMilliseconds() > 2.0) {
1897
// Account for time drift here where vsync never quite catches up to