GfxInfo.cpp 24.8 KB
Newer Older
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3
4
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6

#include "GfxInfo.h"
7
#include "GLContext.h"
8
#include "GLContextProvider.h"
9
10
#include "nsUnicharUtils.h"
#include "prenv.h"
11
#include "nsExceptionHandler.h"
12
#include "nsHashKeys.h"
13
#include "nsVersionComparator.h"
14
#include "AndroidBridge.h"
15
#include "nsServiceManagerUtils.h"
16

17
18
#include "mozilla/Preferences.h"

19
20
#define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1"

21
22
23
namespace mozilla {
namespace widget {

24
class GfxInfo::GLStrings {
25
26
27
  nsCString mVendor;
  nsCString mRenderer;
  nsCString mVersion;
28
  nsTArray<nsCString> mExtensions;
29
30
  bool mReady;

31
32
 public:
  GLStrings() : mReady(false) {}
33
34
35
36
37
38

  const nsCString& Vendor() {
    EnsureInitialized();
    return mVendor;
  }

39
40
  // This spoofed value wins, even if the environment variable
  // MOZ_GFX_SPOOF_GL_VENDOR was set.
41
  void SpoofVendor(const nsCString& s) { mVendor = s; }
42
43
44
45
46
47

  const nsCString& Renderer() {
    EnsureInitialized();
    return mRenderer;
  }

48
49
  // This spoofed value wins, even if the environment variable
  // MOZ_GFX_SPOOF_GL_RENDERER was set.
50
  void SpoofRenderer(const nsCString& s) { mRenderer = s; }
51
52
53
54
55
56

  const nsCString& Version() {
    EnsureInitialized();
    return mVersion;
  }

57
58
  // This spoofed value wins, even if the environment variable
  // MOZ_GFX_SPOOF_GL_VERSION was set.
59
  void SpoofVersion(const nsCString& s) { mVersion = s; }
60

61
62
63
64
65
  const nsTArray<nsCString>& Extensions() {
    EnsureInitialized();
    return mExtensions;
  }

66
  void EnsureInitialized() {
67
68
69
70
    if (mReady) {
      return;
    }

71
    RefPtr<gl::GLContext> gl;
72
    nsCString discardFailureId;
73
    gl = gl::GLContextProvider::CreateHeadless(
74
        {gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE}, &discardFailureId);
75

76
77
    if (!gl) {
      // Setting mReady to true here means that we won't retry. Everything will
78
      // remain blocklisted forever. Ideally, we would like to update that once
79
      // any GLContext is successfully created, like the compositor's GLContext.
80
      mReady = true;
81
      return;
82
    }
83
84
85

    gl->MakeCurrent();

86
    if (mVendor.IsEmpty()) {
87
      const char* spoofedVendor = PR_GetEnv("MOZ_GFX_SPOOF_GL_VENDOR");
88
      if (spoofedVendor) {
89
        mVendor.Assign(spoofedVendor);
90
      } else {
91
        mVendor.Assign((const char*)gl->fGetString(LOCAL_GL_VENDOR));
92
93
94
95
      }
    }

    if (mRenderer.IsEmpty()) {
96
      const char* spoofedRenderer = PR_GetEnv("MOZ_GFX_SPOOF_GL_RENDERER");
97
      if (spoofedRenderer) {
98
        mRenderer.Assign(spoofedRenderer);
99
      } else {
100
        mRenderer.Assign((const char*)gl->fGetString(LOCAL_GL_RENDERER));
101
102
103
104
      }
    }

    if (mVersion.IsEmpty()) {
105
      const char* spoofedVersion = PR_GetEnv("MOZ_GFX_SPOOF_GL_VERSION");
106
      if (spoofedVersion) {
107
        mVersion.Assign(spoofedVersion);
108
      } else {
109
        mVersion.Assign((const char*)gl->fGetString(LOCAL_GL_VERSION));
110
111
      }
    }
112

113
114
115
116
117
118
119
120
121
122
    if (mExtensions.IsEmpty()) {
      nsCString rawExtensions;
      rawExtensions.Assign((const char*)gl->fGetString(LOCAL_GL_EXTENSIONS));
      rawExtensions.Trim(" ");

      for (auto extension : rawExtensions.Split(' ')) {
        mExtensions.AppendElement(extension);
      }
    }

123
    mReady = true;
124
125
  }
};
126

127
#ifdef DEBUG
128
NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug)
129
130
#endif

131
GfxInfo::GfxInfo()
132
133
134
135
    : mInitialized(false),
      mGLStrings(new GLStrings),
      mOSVersionInteger(0),
      mSDKVersion(0) {}
136

137
GfxInfo::~GfxInfo() {}
138

139
140
141
142
/* GetD2DEnabled and GetDwriteEnabled shouldn't be called until after
 * gfxPlatform initialization has occurred because they depend on it for
 * information. (See bug 591561) */
nsresult GfxInfo::GetD2DEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; }
143

144
nsresult GfxInfo::GetDWriteEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; }
145

146
147
148
149
nsresult GfxInfo::GetHasBattery(bool* aHasBattery) {
  return NS_ERROR_NOT_IMPLEMENTED;
}

150
NS_IMETHODIMP
151
GfxInfo::GetDWriteVersion(nsAString& aDwriteVersion) {
152
  return NS_ERROR_FAILURE;
153
154
}

155
156
157
158
159
NS_IMETHODIMP
GfxInfo::GetEmbeddedInFirefoxReality(bool* aEmbeddedInFirefoxReality) {
  return NS_ERROR_FAILURE;
}

160
NS_IMETHODIMP
161
GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) {
162
  return NS_ERROR_FAILURE;
163
164
}

165
166
NS_IMETHODIMP
GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) {
167
168
169
170
171
172
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
GfxInfo::GetDesktopEnvironment(nsAString& aDesktopEnvironment) {
  return NS_ERROR_NOT_IMPLEMENTED;
173
174
}

175
176
177
NS_IMETHODIMP
GfxInfo::GetTestType(nsAString& aTestType) { return NS_ERROR_NOT_IMPLEMENTED; }

178
179
void GfxInfo::EnsureInitialized() {
  if (mInitialized) return;
180

181
182
183
184
  if (!mozilla::AndroidBridge::Bridge()) {
    gfxWarning() << "AndroidBridge missing during initialization";
    return;
  }
185

186
187
188
189
  if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build",
                                                             "MODEL", mModel)) {
    mAdapterDescription.AppendPrintf("Model: %s",
                                     NS_LossyConvertUTF16toASCII(mModel).get());
190
191
  }

192
193
194
195
  if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
          "android/os/Build", "PRODUCT", mProduct)) {
    mAdapterDescription.AppendPrintf(
        ", Product: %s", NS_LossyConvertUTF16toASCII(mProduct).get());
196
  }
197

198
199
200
201
  if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
          "android/os/Build", "MANUFACTURER", mManufacturer)) {
    mAdapterDescription.AppendPrintf(
        ", Manufacturer: %s", NS_LossyConvertUTF16toASCII(mManufacturer).get());
202
203
  }

204
205
206
207
  if (mozilla::AndroidBridge::Bridge()->GetStaticIntField(
          "android/os/Build$VERSION", "SDK_INT", &mSDKVersion)) {
    // the HARDWARE field isn't available on Android SDK < 8, but we require 9+
    // anyway.
208
    MOZ_ASSERT(mSDKVersion >= 8);
209
210
211
212
    if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
            "android/os/Build", "HARDWARE", mHardware)) {
      mAdapterDescription.AppendPrintf(
          ", Hardware: %s", NS_LossyConvertUTF16toASCII(mHardware).get());
213
214
215
    }
  } else {
    mSDKVersion = 0;
216
  }
217

218
  nsString release;
219
220
  mozilla::AndroidBridge::Bridge()->GetStaticStringField(
      "android/os/Build$VERSION", "RELEASE", release);
221
  mOSVersion = NS_LossyConvertUTF16toASCII(release);
222

223
224
225
226
227
228
229
  mOSVersionInteger = 0;
  char a[5], b[5], c[5], d[5];
  SplitDriverVersion(mOSVersion.get(), a, b, c, d);
  uint8_t na = atoi(a);
  uint8_t nb = atoi(b);
  uint8_t nc = atoi(c);
  uint8_t nd = atoi(d);
230

231
232
  mOSVersionInteger = (uint32_t(na) << 24) | (uint32_t(nb) << 16) |
                      (uint32_t(nc) << 8) | uint32_t(nd);
233

234
235
236
  mAdapterDescription.AppendPrintf(
      ", OpenGL: %s -- %s -- %s", mGLStrings->Vendor().get(),
      mGLStrings->Renderer().get(), mGLStrings->Version().get());
237

238
  AddCrashReportAnnotations();
239

240
241
  mScreenInfo.mScreenDimensions =
      mozilla::AndroidBridge::Bridge()->getScreenSize();
242

243
  mInitialized = true;
244
245
246
}

NS_IMETHODIMP
247
GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) {
248
  EnsureInitialized();
249
  aAdapterDescription = NS_ConvertASCIItoUTF16(mAdapterDescription);
250
251
252
  return NS_OK;
}

253
NS_IMETHODIMP
254
GfxInfo::GetAdapterDescription2(nsAString& aAdapterDescription) {
255
  EnsureInitialized();
256
257
258
  return NS_ERROR_FAILURE;
}

259
NS_IMETHODIMP
260
GfxInfo::GetAdapterRAM(uint32_t* aAdapterRAM) {
261
  EnsureInitialized();
262
  *aAdapterRAM = 0;
263
264
265
  return NS_OK;
}

266
NS_IMETHODIMP
267
GfxInfo::GetAdapterRAM2(uint32_t* aAdapterRAM) {
268
  EnsureInitialized();
269
270
271
  return NS_ERROR_FAILURE;
}

272
NS_IMETHODIMP
273
GfxInfo::GetAdapterDriver(nsAString& aAdapterDriver) {
274
  EnsureInitialized();
275
  aAdapterDriver.Truncate();
276
277
278
  return NS_OK;
}

279
NS_IMETHODIMP
280
GfxInfo::GetAdapterDriver2(nsAString& aAdapterDriver) {
281
  EnsureInitialized();
282
283
284
  return NS_ERROR_FAILURE;
}

285
286
287
288
289
290
291
292
293
294
295
296
297
NS_IMETHODIMP
GfxInfo::GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) {
  EnsureInitialized();
  aAdapterDriverVendor.Truncate();
  return NS_OK;
}

NS_IMETHODIMP
GfxInfo::GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor) {
  EnsureInitialized();
  return NS_ERROR_FAILURE;
}

298
NS_IMETHODIMP
299
GfxInfo::GetAdapterDriverVersion(nsAString& aAdapterDriverVersion) {
300
  EnsureInitialized();
301
  aAdapterDriverVersion = NS_ConvertASCIItoUTF16(mGLStrings->Version());
302
303
304
  return NS_OK;
}

305
NS_IMETHODIMP
306
GfxInfo::GetAdapterDriverVersion2(nsAString& aAdapterDriverVersion) {
307
  EnsureInitialized();
308
309
310
  return NS_ERROR_FAILURE;
}

311
NS_IMETHODIMP
312
GfxInfo::GetAdapterDriverDate(nsAString& aAdapterDriverDate) {
313
  EnsureInitialized();
314
  aAdapterDriverDate.Truncate();
315
316
317
  return NS_OK;
}

318
NS_IMETHODIMP
319
GfxInfo::GetAdapterDriverDate2(nsAString& aAdapterDriverDate) {
320
  EnsureInitialized();
321
322
323
  return NS_ERROR_FAILURE;
}

324
NS_IMETHODIMP
325
GfxInfo::GetAdapterVendorID(nsAString& aAdapterVendorID) {
326
  EnsureInitialized();
327
  aAdapterVendorID = NS_ConvertASCIItoUTF16(mGLStrings->Vendor());
328
329
330
  return NS_OK;
}

331
NS_IMETHODIMP
332
GfxInfo::GetAdapterVendorID2(nsAString& aAdapterVendorID) {
333
  EnsureInitialized();
334
335
336
  return NS_ERROR_FAILURE;
}

337
NS_IMETHODIMP
338
GfxInfo::GetAdapterDeviceID(nsAString& aAdapterDeviceID) {
339
  EnsureInitialized();
340
  aAdapterDeviceID = NS_ConvertASCIItoUTF16(mGLStrings->Renderer());
341
342
343
  return NS_OK;
}

344
NS_IMETHODIMP
345
GfxInfo::GetAdapterDeviceID2(nsAString& aAdapterDeviceID) {
346
  EnsureInitialized();
347
348
349
  return NS_ERROR_FAILURE;
}

350
NS_IMETHODIMP
351
GfxInfo::GetAdapterSubsysID(nsAString& aAdapterSubsysID) {
352
353
354
355
356
  EnsureInitialized();
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
357
GfxInfo::GetAdapterSubsysID2(nsAString& aAdapterSubsysID) {
358
359
360
361
  EnsureInitialized();
  return NS_ERROR_FAILURE;
}

362
NS_IMETHODIMP
363
GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) {
364
  EnsureInitialized();
365
366
367
  return NS_ERROR_FAILURE;
}

368
369
370
371
NS_IMETHODIMP
GfxInfo::GetDisplayInfo(nsTArray<nsString>& aDisplayInfo) {
  EnsureInitialized();
  nsString displayInfo;
372
373
374
  displayInfo.AppendPrintf("%dx%d",
                           (int32_t)mScreenInfo.mScreenDimensions.width,
                           (int32_t)mScreenInfo.mScreenDimensions.height);
375
376
377
378
  aDisplayInfo.AppendElement(displayInfo);
  return NS_OK;
}

379
380
381
382
383
384
385
386
387
388
389
390
NS_IMETHODIMP
GfxInfo::GetDisplayWidth(nsTArray<uint32_t>& aDisplayWidth) {
  aDisplayWidth.AppendElement((uint32_t)mScreenInfo.mScreenDimensions.width);
  return NS_OK;
}

NS_IMETHODIMP
GfxInfo::GetDisplayHeight(nsTArray<uint32_t>& aDisplayHeight) {
  aDisplayHeight.AppendElement((uint32_t)mScreenInfo.mScreenDimensions.height);
  return NS_OK;
}

391
392
393
394
395
NS_IMETHODIMP
GfxInfo::GetDrmRenderDevice(nsACString& aDrmRenderDevice) {
  return NS_ERROR_NOT_IMPLEMENTED;
}

396
void GfxInfo::AddCrashReportAnnotations() {
397
  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID,
398
                                     mGLStrings->Vendor());
399
  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID,
400
                                     mGLStrings->Renderer());
401
  CrashReporter::AnnotateCrashReport(
402
      CrashReporter::Annotation::AdapterDriverVersion, mGLStrings->Version());
403
404
}

405
const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() {
406
  if (sDriverInfo->IsEmpty()) {
407
    APPEND_TO_DRIVER_BLOCKLIST2(
408
409
410
411
        OperatingSystem::Android, DeviceFamily::All,
        nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_STATUS_OK,
        DRIVER_COMPARISON_IGNORED, GfxDriverInfo::allDriverVersions,
        "FEATURE_OK_FORCE_OPENGL");
412
  }
413

414
  return *sDriverInfo;
415
416
}

417
418
419
420
nsresult GfxInfo::GetFeatureStatusImpl(
    int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedDriverVersion,
    const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
    OperatingSystem* aOS /* = nullptr */) {
421
  NS_ENSURE_ARG_POINTER(aStatus);
422
  aSuggestedDriverVersion.SetIsVoid(true);
423
  *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
424
  OperatingSystem os = mOS;
425
  if (aOS) *aOS = os;
426

427
  if (sShutdownOccurred) {
428
429
430
    return NS_OK;
  }

431
  // OpenGL layers are never blocklisted on Android.
432
433
  // This early return is so we avoid potentially slow
  // GLStrings initialization on startup when we initialize GL layers.
434
  if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS) {
435
    *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
436
    return NS_OK;
437
438
  }

439
440
  EnsureInitialized();

441
442
443
444
445
  if (mGLStrings->Vendor().IsEmpty() || mGLStrings->Renderer().IsEmpty()) {
    *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
    return NS_OK;
  }

446
447
  // Don't evaluate special cases when evaluating the downloaded blocklist.
  if (aDriverInfo.IsEmpty()) {
448
    if (aFeature == nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION) {
449
450
      if (mSDKVersion < 11) {
        // It's slower than software due to not having a compositing fast path
451
452
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
        aFailureId = "FEATURE_FAILURE_CANVAS_2D_SDK";
453
454
455
456
457
458
      } else if (mGLStrings->Renderer().Find("Vivante GC1000") != -1) {
        // Blocklist Vivante GC1000. See bug 1248183.
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
        aFailureId = "FEATURE_FAILED_CANVAS_2D_HW";
      } else {
        *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
459
      }
460
461
462
      return NS_OK;
    }

463
    if (aFeature == FEATURE_WEBGL_OPENGL) {
464
      if (mGLStrings->Renderer().Find("Adreno 200") != -1 ||
465
          mGLStrings->Renderer().Find("Adreno 205") != -1) {
466
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
467
        aFailureId = "FEATURE_FAILURE_ADRENO_20x";
468
469
        return NS_OK;
      }
470

471
472
473
474
475
476
477
478
      if (mSDKVersion <= 17) {
        if (mGLStrings->Renderer().Find("Adreno (TM) 3") != -1) {
          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
          aFailureId = "FEATURE_FAILURE_ADRENO_3xx";
        }
        return NS_OK;
      }

479
      if (mHardware.EqualsLiteral("ville")) {
480
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
481
        aFailureId = "FEATURE_FAILURE_VILLE";
482
483
        return NS_OK;
      }
484
    }
485
486
487
488

    if (aFeature == FEATURE_STAGEFRIGHT) {
      NS_LossyConvertUTF16toASCII cManufacturer(mManufacturer);
      NS_LossyConvertUTF16toASCII cModel(mModel);
489
490
      NS_LossyConvertUTF16toASCII cHardware(mHardware);

491
492
493
494
495
      if (cHardware.EqualsLiteral("antares") ||
          cHardware.EqualsLiteral("harmony") ||
          cHardware.EqualsLiteral("picasso") ||
          cHardware.EqualsLiteral("picasso_e") ||
          cHardware.EqualsLiteral("ventana") ||
496
          cHardware.EqualsLiteral("rk30board")) {
497
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
498
        aFailureId = "FEATURE_FAILURE_STAGE_HW";
499
500
501
        return NS_OK;
      }

502
      if (CompareVersions(mOSVersion.get(), "4.1.0") < 0) {
503
        // Whitelist:
504
        //   All Samsung ICS devices, except for:
505
506
507
        //     Samsung SGH-I717 (Bug 845729)
        //     Samsung SGH-I727 (Bug 845729)
        //     Samsung SGH-I757 (Bug 845729)
508
509
        //   All Galaxy nexus ICS devices
        //   Sony Xperia Ion (LT28) ICS devices
510
        bool isWhitelisted =
511
            cModel.Equals("LT28h", nsCaseInsensitiveCStringComparator) ||
512
            cManufacturer.Equals("samsung",
513
                                 nsCaseInsensitiveCStringComparator) ||
514
515
            cModel.Equals(
                "galaxy nexus",
516
517
518
                nsCaseInsensitiveCStringComparator);  // some Galaxy Nexus
                                                      // have
                                                      // manufacturer=amazon
519

520
521
        if (cModel.Find("SGH-I717", true) != -1 ||
            cModel.Find("SGH-I727", true) != -1 ||
522
            cModel.Find("SGH-I757", true) != -1) {
523
524
525
          isWhitelisted = false;
        }

526
527
        if (!isWhitelisted) {
          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
528
          aFailureId = "FEATURE_FAILURE_4_1_HW";
529
530
          return NS_OK;
        }
531
      } else if (CompareVersions(mOSVersion.get(), "4.2.0") < 0) {
532
533
534
        // Whitelist:
        //   All JB phones except for those in blocklist below
        // Blocklist:
535
        //   Samsung devices from bug 812881 and 853522.
536
        //   Motorola XT890 from bug 882342.
537
538
539
540
541
542
543
        bool isBlocklisted = cModel.Find("GT-P3100", true) != -1 ||
                             cModel.Find("GT-P3110", true) != -1 ||
                             cModel.Find("GT-P3113", true) != -1 ||
                             cModel.Find("GT-P5100", true) != -1 ||
                             cModel.Find("GT-P5110", true) != -1 ||
                             cModel.Find("GT-P5113", true) != -1 ||
                             cModel.Find("XT890", true) != -1;
544
545
546

        if (isBlocklisted) {
          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
547
          aFailureId = "FEATURE_FAILURE_4_2_HW";
548
549
          return NS_OK;
        }
550
      } else if (CompareVersions(mOSVersion.get(), "4.3.0") < 0) {
551
552
553
        // Blocklist all Sony devices
        if (cManufacturer.Find("Sony", true) != -1) {
          *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
554
          aFailureId = "FEATURE_FAILURE_4_3_SONY";
555
556
557
          return NS_OK;
        }
      }
558
    }
559

560
561
    if (aFeature == FEATURE_WEBRTC_HW_ACCELERATION_ENCODE) {
      if (mozilla::AndroidBridge::Bridge()) {
562
        *aStatus = WebRtcHwVp8EncodeSupported();
563
        aFailureId = "FEATURE_FAILURE_WEBRTC_ENCODE";
564
        return NS_OK;
565
566
567
568
      }
    }
    if (aFeature == FEATURE_WEBRTC_HW_ACCELERATION_DECODE) {
      if (mozilla::AndroidBridge::Bridge()) {
569
        *aStatus = WebRtcHwVp8DecodeSupported();
570
        aFailureId = "FEATURE_FAILURE_WEBRTC_DECODE";
571
572
573
        return NS_OK;
      }
    }
574
575
576
577
578
579
580
    if (aFeature == FEATURE_WEBRTC_HW_ACCELERATION_H264) {
      if (mozilla::AndroidBridge::Bridge()) {
        *aStatus = WebRtcHwH264Supported();
        aFailureId = "FEATURE_FAILURE_WEBRTC_H264";
        return NS_OK;
      }
    }
581
582
    if (aFeature == FEATURE_VP8_HW_DECODE ||
        aFeature == FEATURE_VP9_HW_DECODE) {
583
584
      NS_LossyConvertUTF16toASCII model(mModel);
      bool isBlocked =
585
          // GIFV crash, see bug 1232911.
586
          model.Equals("GT-N8013", nsCaseInsensitiveCStringComparator);
587

588
589
590
591
592
593
      if (isBlocked) {
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
        aFailureId = "FEATURE_FAILURE_VPx";
      } else {
        *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
      }
594
595
      return NS_OK;
    }
596
597

    if (aFeature == FEATURE_WEBRENDER) {
598
599
      bool isUnblocked = false;
      const nsCString& gpu = mGLStrings->Renderer();
600
      NS_LossyConvertUTF16toASCII model(mModel);
601
602

#ifdef NIGHTLY_BUILD
603
604
      // On Nightly enable Webrender on all Adreno 4xx GPUs
      isUnblocked |= gpu.Find("Adreno (TM) 4", /*ignoreCase*/ true) >= 0;
605
#endif
606
607
608
      // Enable Webrender on all Adreno 5xx and 6xx GPUs
      isUnblocked |= gpu.Find("Adreno (TM) 5", /*ignoreCase*/ true) >= 0 ||
                     gpu.Find("Adreno (TM) 6", /*ignoreCase*/ true) >= 0;
609

610
611
612
      // Enable Webrender on all Mali-Txxx GPUs
      isUnblocked |= gpu.Find("Mali-T", /*ignoreCase*/ true) >= 0;

613
      // Enable Webrender on all Mali-Gxx GPUs...
614
      isUnblocked |= gpu.Find("Mali-G", /*ignoreCase*/ true) >= 0 &&
615
                     // Excluding G31 due to bug 1689947.
616
                     gpu.Find("Mali-G31", /*ignoreCase*/ true) == kNotFound;
617

618
      if (!isUnblocked) {
619
620
621
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
        aFailureId = "FEATURE_FAILURE_WEBRENDER_BLOCKED_DEVICE";
      } else {
622
        *aStatus = nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
623
624
625
      }
      return NS_OK;
    }
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641

    if (aFeature == FEATURE_WEBRENDER_SCISSORED_CACHE_CLEARS) {
      // Emulator with SwiftShader is buggy when attempting to clear picture
      // cache textures with a scissor rect set.
      const bool isEmulatorSwiftShader =
          mGLStrings->Renderer().Find(
              "Android Emulator OpenGL ES Translator (Google SwiftShader)") >=
          0;
      if (isEmulatorSwiftShader) {
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
        aFailureId = "FEATURE_FAILURE_BUG_1603515";
      } else {
        *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
      }
      return NS_OK;
    }
642
643

    if (aFeature == FEATURE_WEBRENDER_OPTIMIZED_SHADERS) {
644
645
646
647
648
      // Optimized shaders result in completely broken rendering on some Mali-T
      // devices. We have seen this on T6xx, T7xx, and T8xx on android versions
      // up to 5.1, and on T6xx on versions up to android 7.1. As a precaution
      // disable for all Mali-T regardless of version. See bug 1689064 and bug
      // 1707283 for details.
649
650
      const bool isMaliT =
          mGLStrings->Renderer().Find("Mali-T", /*ignoreCase*/ true) >= 0;
651
      if (isMaliT) {
652
653
654
655
656
657
658
        *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
        aFailureId = "FEATURE_FAILURE_BUG_1689064";
      } else {
        *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
      }
      return NS_OK;
    }
659
660
  }

661
662
  return GfxInfoBase::GetFeatureStatusImpl(
      aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os);
663
}
664

665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
static nsCString FeatureCacheOsVerPrefName(int32_t aFeature) {
  nsCString osPrefName;
  osPrefName.AppendASCII("gfxinfo.cache.");
  osPrefName.AppendInt(aFeature);
  osPrefName.AppendASCII(".osver");
  return osPrefName;
}

static nsCString FeatureCacheValuePrefName(int32_t aFeature) {
  nsCString osPrefName;
  osPrefName.AppendASCII("gfxinfo.cache.");
  osPrefName.AppendInt(aFeature);
  osPrefName.AppendASCII(".value");
  return osPrefName;
}

static bool GetCachedFeatureVal(int32_t aFeature, uint32_t aExpectedOsVer,
                                int32_t& aOutStatus) {
  uint32_t osVer = 0;
  nsresult rv =
      Preferences::GetUint(FeatureCacheOsVerPrefName(aFeature).get(), &osVer);
  if (NS_FAILED(rv) || osVer != aExpectedOsVer) {
    return false;
  }
  int32_t status = 0;
  rv = Preferences::GetInt(FeatureCacheValuePrefName(aFeature).get(), &status);
  if (NS_FAILED(rv)) {
    return false;
  }
  aOutStatus = status;
  return true;
}

static void SetCachedFeatureVal(int32_t aFeature, uint32_t aOsVer,
                                int32_t aStatus) {
  // Ignore failures; not much we can do anyway.
  Preferences::SetUint(FeatureCacheOsVerPrefName(aFeature).get(), aOsVer);
  Preferences::SetInt(FeatureCacheValuePrefName(aFeature).get(), aStatus);
}

705
int32_t GfxInfo::WebRtcHwVp8EncodeSupported() {
706
707
  MOZ_ASSERT(mozilla::AndroidBridge::Bridge());

708
  // The Android side of this calculation is very slow, so we cache the result
709
710
711
712
713
714
715
716
  // in preferences, invalidating if the OS version changes.

  int32_t status = 0;
  if (GetCachedFeatureVal(FEATURE_WEBRTC_HW_ACCELERATION_ENCODE,
                          mOSVersionInteger, status)) {
    return status;
  }

717
  status = mozilla::AndroidBridge::Bridge()->HasHWVP8Encoder()
718
719
720
721
722
723
724
725
726
               ? nsIGfxInfo::FEATURE_STATUS_OK
               : nsIGfxInfo::FEATURE_BLOCKED_DEVICE;

  SetCachedFeatureVal(FEATURE_WEBRTC_HW_ACCELERATION_ENCODE, mOSVersionInteger,
                      status);

  return status;
}

727
int32_t GfxInfo::WebRtcHwVp8DecodeSupported() {
728
729
730
731
732
733
734
735
736
737
738
  MOZ_ASSERT(mozilla::AndroidBridge::Bridge());

  // The Android side of this caclulation is very slow, so we cache the result
  // in preferences, invalidating if the OS version changes.

  int32_t status = 0;
  if (GetCachedFeatureVal(FEATURE_WEBRTC_HW_ACCELERATION_DECODE,
                          mOSVersionInteger, status)) {
    return status;
  }

739
  status = mozilla::AndroidBridge::Bridge()->HasHWVP8Decoder()
740
741
742
743
744
745
746
747
               ? nsIGfxInfo::FEATURE_STATUS_OK
               : nsIGfxInfo::FEATURE_BLOCKED_DEVICE;

  SetCachedFeatureVal(FEATURE_WEBRTC_HW_ACCELERATION_DECODE, mOSVersionInteger,
                      status);

  return status;
}
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770

int32_t GfxInfo::WebRtcHwH264Supported() {
  MOZ_ASSERT(mozilla::AndroidBridge::Bridge());

  // The Android side of this calculation is very slow, so we cache the result
  // in preferences, invalidating if the OS version changes.

  int32_t status = 0;
  if (GetCachedFeatureVal(FEATURE_WEBRTC_HW_ACCELERATION_H264,
                          mOSVersionInteger, status)) {
    return status;
  }

  status = mozilla::AndroidBridge::Bridge()->HasHWH264()
               ? nsIGfxInfo::FEATURE_STATUS_OK
               : nsIGfxInfo::FEATURE_BLOCKED_DEVICE;

  SetCachedFeatureVal(FEATURE_WEBRTC_HW_ACCELERATION_H264, mOSVersionInteger,
                      status);

  return status;
}

771
772
773
774
#ifdef DEBUG

// Implement nsIGfxInfoDebug

775
NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString& aVendorID) {
776
  mGLStrings->SpoofVendor(NS_LossyConvertUTF16toASCII(aVendorID));
777
778
779
  return NS_OK;
}

780
NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString& aDeviceID) {
781
  mGLStrings->SpoofRenderer(NS_LossyConvertUTF16toASCII(aDeviceID));
782
783
784
  return NS_OK;
}

785
NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString& aDriverVersion) {
786
  mGLStrings->SpoofVersion(NS_LossyConvertUTF16toASCII(aDriverVersion));
787
788
789
  return NS_OK;
}

790
NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) {
791
  EnsureInitialized();
792
  mOSVersion = aVersion;
793
794
795
  return NS_OK;
}

796
NS_IMETHODIMP GfxInfo::FireTestProcess() { return NS_OK; }
797

798
#endif
799

800
nsString GfxInfo::Model() {
801
  EnsureInitialized();
802
803
804
  return mModel;
}

805
nsString GfxInfo::Hardware() {
806
  EnsureInitialized();
807
808
809
  return mHardware;
}

810
nsString GfxInfo::Product() {
811
  EnsureInitialized();
812
813
814
  return mProduct;
}

815
nsString GfxInfo::Manufacturer() {
816
  EnsureInitialized();
817
818
  return mManufacturer;
}
819

820
uint32_t GfxInfo::OperatingSystemVersion() {
821
  EnsureInitialized();
822
823
  return mOSVersionInteger;
}
824

825
826
}  // namespace widget
}  // namespace mozilla