Bug 1196411 - Disable DXVA on 60fps 1080p videos for AMD cards that can't decode quick enough. r=jya, a=ritu
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 20 Aug 2015 11:37:26 -0400
changeset 288944 35805520619521e3f52a3a150d55e88efe8bc3eb
parent 288943 9bd80eff00dd3198163c06dd4d5a159e379f1d56
child 288945 d75ede2c22a4134728b09a9c76be4e2759a1f214
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, ritu
bugs1196411
milestone42.0a2
Bug 1196411 - Disable DXVA on 60fps 1080p videos for AMD cards that can't decode quick enough. r=jya, a=ritu
dom/media/platforms/wmf/DXVA2Manager.cpp
dom/media/platforms/wmf/DXVA2Manager.h
dom/media/platforms/wmf/WMFVideoMFTManager.cpp
dom/media/platforms/wmf/WMFVideoMFTManager.h
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -29,16 +29,45 @@ const GUID MF_XVP_PLAYBACK_MODE =
   0xad67,
   0x4e29,
   { 0xaf, 0x12, 0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9 }
 };
 
 DEFINE_GUID(MF_LOW_LATENCY,
   0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee);
 
+// R600, R700, Evergreen and Cayman AMD cards. These support DXVA via UVD3 or earlier, and don't
+// handle 1080p60 well.
+static const DWORD sAMDPreUVD4[] = {
+  0x9400, 0x9401, 0x9402, 0x9403, 0x9405, 0x940a, 0x940b, 0x940f, 0x94c0, 0x94c1, 0x94c3, 0x94c4, 0x94c5,
+  0x94c6, 0x94c7, 0x94c8, 0x94c9, 0x94cb, 0x94cc, 0x94cd, 0x9580, 0x9581, 0x9583, 0x9586, 0x9587, 0x9588,
+  0x9589, 0x958a, 0x958b, 0x958c, 0x958d, 0x958e, 0x958f, 0x9500, 0x9501, 0x9504, 0x9505, 0x9506, 0x9507,
+  0x9508, 0x9509, 0x950f, 0x9511, 0x9515, 0x9517, 0x9519, 0x95c0, 0x95c2, 0x95c4, 0x95c5, 0x95c6, 0x95c7,
+  0x95c9, 0x95cc, 0x95cd, 0x95ce, 0x95cf, 0x9590, 0x9591, 0x9593, 0x9595, 0x9596, 0x9597, 0x9598, 0x9599,
+  0x959b, 0x9610, 0x9611, 0x9612, 0x9613, 0x9614, 0x9615, 0x9616, 0x9710, 0x9711, 0x9712, 0x9713, 0x9714,
+  0x9715, 0x9440, 0x9441, 0x9442, 0x9443, 0x9444, 0x9446, 0x944a, 0x944b, 0x944c, 0x944e, 0x9450, 0x9452,
+  0x9456, 0x945a, 0x945b, 0x945e, 0x9460, 0x9462, 0x946a, 0x946b, 0x947a, 0x947b, 0x9480, 0x9487, 0x9488,
+  0x9489, 0x948a, 0x948f, 0x9490, 0x9491, 0x9495, 0x9498, 0x949c, 0x949e, 0x949f, 0x9540, 0x9541, 0x9542,
+  0x954e, 0x954f, 0x9552, 0x9553, 0x9555, 0x9557, 0x955f, 0x94a0, 0x94a1, 0x94a3, 0x94b1, 0x94b3, 0x94b4,
+  0x94b5, 0x94b9, 0x68e0, 0x68e1, 0x68e4, 0x68e5, 0x68e8, 0x68e9, 0x68f1, 0x68f2, 0x68f8, 0x68f9, 0x68fa,
+  0x68fe, 0x68c0, 0x68c1, 0x68c7, 0x68c8, 0x68c9, 0x68d8, 0x68d9, 0x68da, 0x68de, 0x68a0, 0x68a1, 0x68a8,
+  0x68a9, 0x68b0, 0x68b8, 0x68b9, 0x68ba, 0x68be, 0x68bf, 0x6880, 0x6888, 0x6889, 0x688a, 0x688c, 0x688d,
+  0x6898, 0x6899, 0x689b, 0x689e, 0x689c, 0x689d, 0x9802, 0x9803, 0x9804, 0x9805, 0x9806, 0x9807, 0x9808,
+  0x9809, 0x980a, 0x9640, 0x9641, 0x9647, 0x9648, 0x964a, 0x964b, 0x964c, 0x964e, 0x964f, 0x9642, 0x9643,
+  0x9644, 0x9645, 0x9649, 0x6720, 0x6721, 0x6722, 0x6723, 0x6724, 0x6725, 0x6726, 0x6727, 0x6728, 0x6729,
+  0x6738, 0x6739, 0x673e, 0x6740, 0x6741, 0x6742, 0x6743, 0x6744, 0x6745, 0x6746, 0x6747, 0x6748, 0x6749,
+  0x674a, 0x6750, 0x6751, 0x6758, 0x6759, 0x675b, 0x675d, 0x675f, 0x6840, 0x6841, 0x6842, 0x6843, 0x6849,
+  0x6850, 0x6858, 0x6859, 0x6760, 0x6761, 0x6762, 0x6763, 0x6764, 0x6765, 0x6766, 0x6767, 0x6768, 0x6770,
+  0x6771, 0x6772, 0x6778, 0x6779, 0x677b, 0x6700, 0x6701, 0x6702, 0x6703, 0x6704, 0x6705, 0x6706, 0x6707,
+  0x6708, 0x6709, 0x6718, 0x6719, 0x671c, 0x671d, 0x671f, 0x9900, 0x9901, 0x9903, 0x9904, 0x9905, 0x9906,
+  0x9907, 0x9908, 0x9909, 0x990a, 0x990b, 0x990c, 0x990d, 0x990e, 0x990f, 0x9910, 0x9913, 0x9917, 0x9918,
+  0x9919, 0x9990, 0x9991, 0x9992, 0x9993, 0x9994, 0x9995, 0x9996, 0x9997, 0x9998, 0x9999, 0x999a, 0x999b,
+  0x999c, 0x999d, 0x99a0, 0x99a2, 0x99a4
+};
+
 namespace mozilla {
 
 using layers::Image;
 using layers::ImageContainer;
 using layers::D3D9SurfaceImage;
 using layers::D3D11ShareHandleImage;
 
 class D3D9DXVA2Manager : public DXVA2Manager
@@ -53,24 +82,25 @@ public:
 
   // Copies a region (aRegion) of the video frame stored in aVideoSample
   // into an image which is returned by aOutImage.
   HRESULT CopyToImage(IMFSample* aVideoSample,
                       const nsIntRect& aRegion,
                       ImageContainer* aContainer,
                       Image** aOutImage) override;
 
-  virtual bool SupportsConfig(IMFMediaType* aType) override;
+  virtual bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
 
 private:
   nsRefPtr<IDirect3D9Ex> mD3D9;
   nsRefPtr<IDirect3DDevice9Ex> mDevice;
   nsRefPtr<IDirect3DDeviceManager9> mDeviceManager;
   nsRefPtr<IDirectXVideoDecoderService> mDecoderService;
   UINT32 mResetToken;
+  bool mIsAMDPreUVD4;
 };
 
 void GetDXVA2ExtendedFormatFromMFMediaType(IMFMediaType *pType,
                                            DXVA2_ExtendedFormat *pFormat)
 {
   // Get the interlace mode.
   MFVideoInterlaceMode interlace =
     (MFVideoInterlaceMode)MFGetAttributeUINT32(pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Unknown);
@@ -133,22 +163,31 @@ static const GUID DXVA2_ModeH264_E = {
   0x1b81be68, 0xa0c7, 0x11d3, { 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5 }
 };
 
 // This tests if a DXVA video decoder can be created for the given media type/resolution.
 // It uses the same decoder device (DXVA2_ModeH264_E - DXVA2_ModeH264_VLD_NoFGT) as the H264
 // decoder MFT provided by windows (CLSID_CMSH264DecoderMFT) uses, so we can use it to determine
 // if the MFT will use software fallback or not.
 bool
-D3D9DXVA2Manager::SupportsConfig(IMFMediaType* aType)
+D3D9DXVA2Manager::SupportsConfig(IMFMediaType* aType, float aFramerate)
 {
   DXVA2_VideoDesc desc;
   HRESULT hr = ConvertMFTypeToDXVAType(aType, &desc);
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
+  // AMD cards with UVD3 or earlier perform poorly trying to decode 1080p60 in hardware,
+  // so use software instead. Pick 45 as an arbitrary upper bound for the framerate we can
+  // handle.
+  if (mIsAMDPreUVD4 &&
+      (desc.SampleWidth >= 1920 || desc.SampleHeight >= 1088) &&
+      aFramerate > 45) {
+    return false;
+  }
+
   UINT configCount;
   DXVA2_ConfigPictureDecode* configs = nullptr;
   hr = mDecoderService->GetDecoderConfigurations(DXVA2_ModeH264_E, &desc, nullptr, &configCount, &configs);
   NS_ENSURE_TRUE(SUCCEEDED(hr), false);
 
   nsRefPtr<IDirect3DSurface9> surface;
   hr = mDecoderService->CreateSurface(desc.SampleWidth, desc.SampleHeight, 0, (D3DFORMAT)MAKEFOURCC('N', 'V', '1', '2'),
   D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget,
@@ -168,16 +207,17 @@ D3D9DXVA2Manager::SupportsConfig(IMFMedi
     }
   }
   CoTaskMemFree(configs);
   return false;
 }
 
 D3D9DXVA2Manager::D3D9DXVA2Manager()
   : mResetToken(0)
+  , mIsAMDPreUVD4(false)
 {
   MOZ_COUNT_CTOR(D3D9DXVA2Manager);
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 D3D9DXVA2Manager::~D3D9DXVA2Manager()
 {
   MOZ_COUNT_DTOR(D3D9DXVA2Manager);
@@ -278,17 +318,30 @@ D3D9DXVA2Manager::Init()
       found = true;
       break;
     }
   }
   CoTaskMemFree(decoderDevices);
 
   if (!found) {
    return E_FAIL;
- }
+  }
+
+  D3DADAPTER_IDENTIFIER9 adapter;
+  hr = d3d9Ex->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &adapter);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
+  if (adapter.VendorId = 0x1022) {
+    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sAMDPreUVD4); i++) {
+      if (adapter.DeviceId == sAMDPreUVD4[i]) {
+        mIsAMDPreUVD4 = true;
+        break;
+      }
+    }
+  }
 
   mDecoderService = decoderService;
 
   mResetToken = resetToken;
   mD3D9 = d3d9Ex;
   mDevice = device;
   mDeviceManager = deviceManager;
 
@@ -369,16 +422,18 @@ public:
   HRESULT CopyToImage(IMFSample* aVideoSample,
                       const nsIntRect& aRegion,
                       ImageContainer* aContainer,
                       Image** aOutImage) override;
 
   virtual HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) override;
 
   virtual bool IsD3D11() override { return true; }
+  
+  virtual bool SupportsConfig(IMFMediaType* aType, float aFramerate) override { return true; }
 
 private:
   HRESULT CreateFormatConverter();
 
   HRESULT CreateOutputSample(RefPtr<IMFSample>& aSample,
                              RefPtr<ID3D11Texture2D>& aTexture);
 
   RefPtr<ID3D11Device> mDevice;
--- a/dom/media/platforms/wmf/DXVA2Manager.h
+++ b/dom/media/platforms/wmf/DXVA2Manager.h
@@ -39,17 +39,17 @@ public:
                               layers::Image** aOutImage) = 0;
 
   virtual HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) { return S_OK; }
 
   virtual bool IsD3D11() { return false; }
 
   virtual ~DXVA2Manager();
 
-  virtual bool SupportsConfig(IMFMediaType* aType) { return true; }
+  virtual bool SupportsConfig(IMFMediaType* aType, float aFramerate) = 0;
 
 protected:
   Mutex mLock;
   DXVA2Manager();
 };
 
 } // namespace mozilla
 
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -287,16 +287,18 @@ WMFVideoMFTManager::Input(MediaRawData* 
   }
 
   HRESULT hr = mDecoder->CreateInputSample(aSample->Data(),
                                            uint32_t(aSample->Size()),
                                            aSample->mTime,
                                            &mLastInput);
   NS_ENSURE_TRUE(SUCCEEDED(hr) && mLastInput != nullptr, hr);
 
+  mLastDuration = aSample->mDuration;
+
   // Forward sample data to the decoder.
   return mDecoder->Input(mLastInput);
 }
 
 // The MFTransform we use for decoding h264 video will silently fall
 // back to software decoding (even if we've negotiated DXVA) if the GPU
 // doesn't support decoding the given resolution. It will then upload
 // the software decoded frames into d3d textures to preserve behaviour.
@@ -310,17 +312,21 @@ WMFVideoMFTManager::Input(MediaRawData* 
 bool
 WMFVideoMFTManager::MaybeToggleDXVA(IMFMediaType* aType)
 {
   // SupportsConfig only checks for valid h264 decoders currently.
   if (!mDXVA2Manager || mStreamType != H264) {
     return false;
   }
 
-  if (mDXVA2Manager->SupportsConfig(aType)) {
+  // Assume the current samples duration is representative for the
+  // entire video.
+  float framerate = 1000000.0 / mLastDuration;
+
+  if (mDXVA2Manager->SupportsConfig(aType, framerate)) {
     if (!mUseHwAccel) {
       // DXVA disabled, but supported for this resolution
       ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
       HRESULT hr = mDecoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
       if (SUCCEEDED(hr)) {
         mUseHwAccel = true;
         return true;
       }
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -63,16 +63,17 @@ private:
   uint32_t mVideoHeight;
   nsIntRect mPictureRegion;
 
   RefPtr<MFTDecoder> mDecoder;
   RefPtr<layers::ImageContainer> mImageContainer;
   nsAutoPtr<DXVA2Manager> mDXVA2Manager;
 
   RefPtr<IMFSample> mLastInput;
+  float mLastDuration;
 
   const bool mDXVAEnabled;
   const layers::LayersBackend mLayersBackend;
   bool mUseHwAccel;
 
   enum StreamType {
     Unknown,
     H264,