Bug 1257013 - Part 2: Use readback to synchronize d3d11 video. r=cpearce, r=Bas
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 21 Mar 2016 18:49:59 +1300
changeset 323961 e040ea9522763d6cf7dbbb124336ff9c37887b40
parent 323960 714808ad8a7d61fb8500de96bc36dee12cb28f69
child 323962 5ff5f2178682a3888ada94fa29409d54b8d4716a
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, Bas
bugs1257013
milestone47.0a2
Bug 1257013 - Part 2: Use readback to synchronize d3d11 video. r=cpearce, r=Bas
dom/media/platforms/wmf/DXVA2Manager.cpp
gfx/layers/D3D11ShareHandleImage.cpp
gfx/layers/client/TextureClient.h
gfx/layers/client/TextureClientRecycleAllocator.cpp
gfx/layers/d3d11/TextureD3D11.cpp
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -518,16 +518,17 @@ private:
   HRESULT CreateOutputSample(RefPtr<IMFSample>& aSample,
                              ID3D11Texture2D* aTexture);
 
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11DeviceContext> mContext;
   RefPtr<IMFDXGIDeviceManager> mDXGIDeviceManager;
   RefPtr<MFTDecoder> mTransform;
   RefPtr<D3D11RecycleAllocator> mTextureClientAllocator;
+  RefPtr<ID3D11Texture2D> mSyncSurface;
   GUID mDecoderGUID;
   uint32_t mWidth;
   uint32_t mHeight;
   UINT mDeviceManagerToken;
   bool mIsAMDPreUVD4;
 };
 
 bool
@@ -697,16 +698,32 @@ D3D11DXVA2Manager::Init(nsACString& aFai
     for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sAMDPreUVD4); i++) {
       if (adapterDesc.DeviceId == sAMDPreUVD4[i]) {
         mIsAMDPreUVD4 = true;
         break;
       }
     }
   }
 
+  D3D11_TEXTURE2D_DESC desc;
+  desc.Width = kSyncSurfaceSize;
+  desc.Height = kSyncSurfaceSize;
+  desc.MipLevels = 1;
+  desc.ArraySize = 1;
+  desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+  desc.SampleDesc.Count = 1;
+  desc.SampleDesc.Quality = 0;
+  desc.Usage = D3D11_USAGE_STAGING;
+  desc.BindFlags = 0;
+  desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+  desc.MiscFlags = 0;
+
+  hr = mDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(mSyncSurface));
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+
   mTextureClientAllocator = new D3D11RecycleAllocator(layers::ImageBridgeChild::GetSingleton(),
                                                       mDevice);
   mTextureClientAllocator->SetMaxPoolSize(5);
 
   return S_OK;
 }
 
 HRESULT
@@ -748,27 +765,31 @@ D3D11DXVA2Manager::CopyToImage(IMFSample
   HRESULT hr = mTransform->Input(aVideoSample);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   RefPtr<IMFSample> sample;
   RefPtr<ID3D11Texture2D> texture = image->GetTexture();
   hr = CreateOutputSample(sample, texture);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  RefPtr<IDXGIKeyedMutex> keyedMutex;
-  hr = texture->QueryInterface(static_cast<IDXGIKeyedMutex**>(getter_AddRefs(keyedMutex)));
-  NS_ENSURE_TRUE(SUCCEEDED(hr) && keyedMutex, hr);
+  hr = mTransform->Output(&sample);
+
+  RefPtr<ID3D11DeviceContext> ctx;
+  mDevice->GetImmediateContext(getter_AddRefs(ctx));
 
-  hr = keyedMutex->AcquireSync(0, INFINITE);
+  // Copy a small rect into our sync surface, and then map it
+  // to block until decoding/color conversion completes.
+  D3D11_BOX rect = { 0, 0, 0, kSyncSurfaceSize, kSyncSurfaceSize, 1 };
+  ctx->CopySubresourceRegion(mSyncSurface, 0, 0, 0, 0, texture, 0, &rect);
+
+  D3D11_MAPPED_SUBRESOURCE mapped;
+  hr = ctx->Map(mSyncSurface, 0, D3D11_MAP_READ, 0, &mapped);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  hr = mTransform->Output(&sample);
-
-  keyedMutex->ReleaseSync(0);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
+  ctx->Unmap(mSyncSurface, 0);
 
   image.forget(aOutImage);
 
   return S_OK;
 }
 
 HRESULT ConfigureOutput(IMFMediaType* aOutput, void* aData)
 {
--- a/gfx/layers/D3D11ShareHandleImage.cpp
+++ b/gfx/layers/D3D11ShareHandleImage.cpp
@@ -50,27 +50,16 @@ D3D11ShareHandleImage::GetAsSourceSurfac
   if (!texture) {
     NS_WARNING("Cannot readback from shared texture because no texture is available.");
     return nullptr;
   }
 
   RefPtr<ID3D11Device> device;
   texture->GetDevice(getter_AddRefs(device));
 
-  RefPtr<IDXGIKeyedMutex> keyedMutex;
-  if (FAILED(texture->QueryInterface(static_cast<IDXGIKeyedMutex**>(getter_AddRefs(keyedMutex))))) {
-    NS_WARNING("Failed to QueryInterface for IDXGIKeyedMutex, strange.");
-    return nullptr;
-  }
-
-  if (FAILED(keyedMutex->AcquireSync(0, 0))) {
-    NS_WARNING("Failed to acquire sync for keyedMutex, plugin failed to release?");
-    return nullptr;
-  }
-
   D3D11_TEXTURE2D_DESC desc;
   texture->GetDesc(&desc);
 
   CD3D11_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height);
   softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
   softDesc.BindFlags = 0;
   softDesc.MiscFlags = 0;
   softDesc.MipLevels = 1;
@@ -78,29 +67,26 @@ D3D11ShareHandleImage::GetAsSourceSurfac
 
   RefPtr<ID3D11Texture2D> softTexture;
   HRESULT hr = device->CreateTexture2D(&softDesc,
                                        NULL,
                                        static_cast<ID3D11Texture2D**>(getter_AddRefs(softTexture)));
 
   if (FAILED(hr)) {
     NS_WARNING("Failed to create 2D staging texture.");
-    keyedMutex->ReleaseSync(0);
     return nullptr;
   }
 
   RefPtr<ID3D11DeviceContext> context;
   device->GetImmediateContext(getter_AddRefs(context));
   if (!context) {
-    keyedMutex->ReleaseSync(0);
     return nullptr;
   }
 
   context->CopyResource(softTexture, texture);
-  keyedMutex->ReleaseSync(0);
 
   RefPtr<gfx::DataSourceSurface> surface =
     gfx::Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
   if (NS_WARN_IF(!surface)) {
     return nullptr;
   }
 
   gfx::DataSourceSurface::MappedSurface mappedSurface;
@@ -147,15 +133,16 @@ D3D11RecycleAllocator::Allocate(gfx::Sur
 already_AddRefed<TextureClient>
 D3D11RecycleAllocator::CreateOrRecycleClient(gfx::SurfaceFormat aFormat,
                                              const gfx::IntSize& aSize)
 {
   RefPtr<TextureClient> textureClient =
     CreateOrRecycle(aFormat,
                     aSize,
                     BackendSelector::Content,
-                    layers::TextureFlags::DEFAULT);
+                    layers::TextureFlags::DEFAULT,
+                    TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION);
   return textureClient.forget();
 }
 
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -75,16 +75,20 @@ enum TextureAllocationFlags {
   ALLOC_CLEAR_BUFFER_WHITE = 1 << 2,  // explicit all white
   ALLOC_CLEAR_BUFFER_BLACK = 1 << 3,  // explicit all black
   ALLOC_DISALLOW_BUFFERTEXTURECLIENT = 1 << 4,
 
   // Allocate the texture for out-of-band content updates. This is mostly for
   // TextureClientD3D11, which may otherwise choose D3D10 or non-KeyedMutex
   // surfaces when used on the main thread.
   ALLOC_FOR_OUT_OF_BAND_CONTENT = 1 << 5,
+
+  // Disable any cross-device synchronization. This is also for TextureClientD3D11,
+  // and creates a texture without KeyedMutex.
+  ALLOC_MANUAL_SYNCHRONIZATION = 1 << 6,
 };
 
 #ifdef XP_WIN
 typedef void* SyncHandle;
 #else
 typedef uintptr_t SyncHandle;
 #endif // XP_WIN
 
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -130,17 +130,18 @@ TextureClientRecycleAllocator::CreateOrR
 
 already_AddRefed<TextureClient>
 TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
 {
   // TextureAllocationFlags is actually used only by ContentClient.
   // This class does not handle ContentClient's TextureClient allocation.
   MOZ_ASSERT(aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DEFAULT ||
              aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT ||
-             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT);
+             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT ||
+             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION);
   MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
 
   RefPtr<TextureClientHolder> textureHolder;
 
   {
     MutexAutoLock lock(mLock);
     if (!mPooledClients.empty()) {
       textureHolder = mPooledClients.top();
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -357,17 +357,19 @@ D3D11TextureData::Create(IntSize aSize, 
 
   CD3D11_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
                                 aSize.width, aSize.height, 1, 1,
                                 D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 
   newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
   if (!NS_IsMainThread() || !!(aFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT)) {
     // On the main thread we use the syncobject to handle synchronization.
-    newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+    if (!(aFlags & ALLOC_MANUAL_SYNCHRONIZATION)) {
+      newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+    }
   }
 
   HRESULT hr = d3d11device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(texture11));
   if (FAILED(hr)) {
     gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
       << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
     return nullptr;
   }