Bug 1419293 - Create SwapChain with DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL if possible in ANGLE r=jgilbert
authorsotaro <sotaro.ikeda.g@gmail.com>
Thu, 01 Feb 2018 21:39:31 +0900
changeset 402068 03d8ada6d1aaab9e10bff42f2297168a2569ee54
parent 402067 c8c3e5505d9c0d591d5cd148ef19400cdcce7b95
child 402069 43a07eda5524f9f4405b7acffe7bb884be09c180
push id33362
push userncsoregi@mozilla.com
push dateThu, 01 Feb 2018 18:12:02 +0000
treeherdermozilla-central@a23212941fcd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1419293
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1419293 - Create SwapChain with DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL if possible in ANGLE r=jgilbert
gfx/gl/GLContextEGL.h
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLDefs.h
gfx/webrender_bindings/RenderCompositor.h
gfx/webrender_bindings/RenderCompositorANGLE.cpp
gfx/webrender_bindings/RenderCompositorANGLE.h
gfx/webrender_bindings/RenderCompositorOGL.cpp
gfx/webrender_bindings/RenderCompositorOGL.h
gfx/webrender_bindings/RendererOGL.cpp
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -134,12 +134,14 @@ protected:
                                                            EGLenum bindToTextureFormat,
                                                            gfx::IntSize& pbsize);
 #if defined(MOZ_WIDGET_ANDROID)
 public:
     EGLSurface CreateCompatibleSurface(void* aWindow);
 #endif // defined(MOZ_WIDGET_ANDROID)
 };
 
+bool CreateConfig(EGLConfig* config, int32_t depth, bool enableDepthBuffer);
+
 } // namespace gl
 } // namespace mozilla
 
 #endif // GLCONTEXTEGL_H_
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -79,19 +79,16 @@ using namespace mozilla::widget;
 
 #define ADD_ATTR_1(_array, _k) do {             \
     (_array).AppendElement(_k);                 \
 } while (0)
 
 static bool
 CreateConfig(EGLConfig* aConfig, bool aEnableDepthBuffer);
 
-static bool
-CreateConfig(EGLConfig* aConfig, int32_t depth, bool aEnableDepthBuffer);
-
 // append three zeros at the end of attribs list to work around
 // EGL implementation bugs that iterate until they find 0, instead of
 // EGL_NONE. See bug 948406.
 #define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
      LOCAL_EGL_NONE, 0, 0, 0
 
 static EGLint kTerminationAttribs[] = {
     EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
@@ -629,17 +626,17 @@ static const EGLint kEGLConfigAttribsRGB
     LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
     LOCAL_EGL_RED_SIZE,        8,
     LOCAL_EGL_GREEN_SIZE,      8,
     LOCAL_EGL_BLUE_SIZE,       8,
     LOCAL_EGL_ALPHA_SIZE,      8,
     EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
 };
 
-static bool
+bool
 CreateConfig(EGLConfig* aConfig, int32_t depth, bool aEnableDepthBuffer)
 {
     EGLConfig configs[64];
     const EGLint* attribs;
     EGLint ncfg = ArrayLength(configs);
 
     switch (depth) {
         case 16:
--- a/gfx/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -48,16 +48,22 @@
 #define LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE              0x3207
 #define LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE             0x3208
 #define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE            0x3209
 #define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE   0x320A
 #define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE       0x320B
 #define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE  0x320C
 #define LOCAL_EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE  0x320F
 
+// EGL_ANGLE_d3d_texture_client_buffer
+#define LOCAL_EGL_D3D_TEXTURE_ANGLE                          0x33A3
+
+// EGL_ANGLE_flexible_surface_compatibility
+#define LOCAL_EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6
+
 // EGL_ANGLE_experimental_present_path
 #define LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE             0x33A4
 #define LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE        0x33A9
 #define LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE        0x33AA
 
 // EGL_ANGLE_direct3d_display
 #define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE         ((EGLNativeDisplayType)-2)
 #define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE              ((EGLNativeDisplayType)-3)
--- a/gfx/webrender_bindings/RenderCompositor.h
+++ b/gfx/webrender_bindings/RenderCompositor.h
@@ -38,17 +38,17 @@ public:
   virtual void EndFrame() = 0;
   virtual void Pause() = 0;
   virtual bool Resume() = 0;
 
   virtual gl::GLContext* gl() const { return nullptr; }
 
   virtual bool UseANGLE() const { return false; }
 
-  virtual LayoutDeviceIntSize GetClientSize() = 0;
+  virtual LayoutDeviceIntSize GetBufferSize() = 0;
 
   widget::CompositorWidget* GetWidget() const { return mWidget; }
 
   layers::SyncObjectHost* GetSyncObject() const { return mSyncObject.get(); }
 
 protected:
   RefPtr<widget::CompositorWidget> mWidget;
   RefPtr<layers::SyncObjectHost> mSyncObject;
--- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -8,34 +8,39 @@
 
 #include "GLContext.h"
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
 #include "mozilla/gfx/DeviceManagerDx.h"
 #include "mozilla/layers/HelpersD3D11.h"
 #include "mozilla/layers/SyncObject.h"
 #include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+#include "mozilla/WindowsVersion.h"
 
 #include <d3d11.h>
+#include <dxgi1_2.h>
 
 namespace mozilla {
 namespace wr {
 
 /* static */ UniquePtr<RenderCompositor>
 RenderCompositorANGLE::Create(RefPtr<widget::CompositorWidget>&& aWidget)
 {
   UniquePtr<RenderCompositorANGLE> compositor = MakeUnique<RenderCompositorANGLE>(Move(aWidget));
   if (!compositor->Initialize()) {
     return nullptr;
   }
   return compositor;
 }
 
 RenderCompositorANGLE::RenderCompositorANGLE(RefPtr<widget::CompositorWidget>&& aWidget)
   : RenderCompositor(Move(aWidget))
+  , mEGLConfig(nullptr)
+  , mEGLSurface(nullptr)
 {
 }
 
 RenderCompositorANGLE::~RenderCompositorANGLE()
 {
 }
 
 bool
@@ -48,45 +53,134 @@ RenderCompositorANGLE::Initialize()
   }
 
   mDevice->GetImmediateContext(getter_AddRefs(mCtx));
   if (!mCtx) {
     gfxCriticalNote << "[D3D11] failed to get immediate context.";
     return false;
   }
 
+  HWND hwnd = mWidget->AsWindows()->GetHwnd();
+
+  RefPtr<IDXGIDevice> dxgiDevice;
+  mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
+
+  RefPtr<IDXGIFactory> dxgiFactory;
+  {
+    RefPtr<IDXGIAdapter> adapter;
+    dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+
+    adapter->GetParent(IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
+  }
+
+  RefPtr<IDXGIFactory2> dxgiFactory2;
+  if (SUCCEEDED(dxgiFactory->QueryInterface((IDXGIFactory2**)getter_AddRefs(dxgiFactory2))) &&
+      dxgiFactory2 &&
+      IsWin8OrLater())
+  {
+    RefPtr<IDXGISwapChain1> swapChain1;
+
+    DXGI_SWAP_CHAIN_DESC1 desc{};
+    desc.Width = 0;
+    desc.Height = 0;
+    desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+    desc.SampleDesc.Count = 1;
+    desc.SampleDesc.Quality = 0;
+    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    desc.BufferCount = 2;
+    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+    desc.Scaling = DXGI_SCALING_NONE;
+    desc.Flags = 0;
+
+    HRESULT hr = dxgiFactory2->CreateSwapChainForHwnd(mDevice, hwnd, &desc,
+                                                      nullptr, nullptr,
+                                                      getter_AddRefs(swapChain1));
+    if (SUCCEEDED(hr) && swapChain1) {
+      DXGI_RGBA color = { 1.0f, 1.0f, 1.0f, 1.0f };
+      swapChain1->SetBackgroundColor(&color);
+      mSwapChain = swapChain1;
+    }
+  }
+
+  if (!mSwapChain) {
+    DXGI_SWAP_CHAIN_DESC swapDesc{};
+    swapDesc.BufferDesc.Width = 0;
+    swapDesc.BufferDesc.Height = 0;
+    swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+    swapDesc.BufferDesc.RefreshRate.Numerator = 60;
+    swapDesc.BufferDesc.RefreshRate.Denominator = 1;
+    swapDesc.SampleDesc.Count = 1;
+    swapDesc.SampleDesc.Quality = 0;
+    swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    swapDesc.BufferCount = 1;
+    swapDesc.OutputWindow = hwnd;
+    swapDesc.Windowed = TRUE;
+    swapDesc.Flags = 0;
+    swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
+
+    HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, getter_AddRefs(mSwapChain));
+    if (FAILED(hr)) {
+      gfxCriticalNote << "Could not create swap chain: " << gfx::hexa(hr);
+      return false;
+    }
+  }
+
+  // We need this because we don't want DXGI to respond to Alt+Enter.
+  dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
+
   mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(mDevice);
   if (!mSyncObject->Init()) {
     // Some errors occur. Clear the mSyncObject here.
     // Then, there will be no texture synchronization.
     return false;
   }
 
-  mGL = gl::GLContextProviderEGL::CreateForCompositorWidget(mWidget, true);
+  const auto flags = gl::CreateContextFlags::PREFER_ES3;
+
+  // Create GLContext with dummy EGLSurface, the EGLSurface is not used.
+  // Instread we override it with EGLSurface of SwapChain's back buffer.
+  nsCString discardFailureId;
+  mGL = gl::GLContextProviderEGL::CreateHeadless(flags, &discardFailureId);
   if (!mGL || !mGL->IsANGLE()) {
     gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: " << gfx::hexa(mGL.get());
     return false;
   }
+
   if (!mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed GL context creation for WebRender: " << gfx::hexa(mGL.get());
     return false;
   }
 
+  // Force enable alpha channel to make sure ANGLE use correct framebuffer formart
+  if (!gl::CreateConfig(&mEGLConfig, /* bpp */ 32, /* enableDepthBuffer */ true)) {
+    gfxCriticalNote << "Failed to create EGLConfig for WebRender";
+  }
+  MOZ_ASSERT(mEGLConfig);
+
+  if (!ResizeBufferIfNeeded()) {
+    return false;
+  }
+
   return true;
 }
 
 bool
 RenderCompositorANGLE::Destroy()
 {
+  DestroyEGLSurface();
   return true;
 }
 
 bool
 RenderCompositorANGLE::BeginFrame()
 {
+  if (!ResizeBufferIfNeeded()) {
+    return false;
+  }
+
   if (!mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
 
   if (mSyncObject) {
     // XXX: if the synchronization is failed, we should handle the device reset.
     mSyncObject->Synchronize();
@@ -94,17 +188,17 @@ RenderCompositorANGLE::BeginFrame()
   return true;
 }
 
 void
 RenderCompositorANGLE::EndFrame()
 {
   InsertPresentWaitQuery();
 
-  mGL->SwapBuffers();
+  mSwapChain->Present(0, 0);
 
   // Note: this waits on the query we inserted in the previous frame,
   // not the one we just inserted now. Example:
   //   Insert query #1
   //   Present #1
   //   (first frame, no wait)
   //   Insert query #2
   //   Present #2
@@ -112,31 +206,123 @@ RenderCompositorANGLE::EndFrame()
   //   Insert query #3
   //   Present #3
   //   Wait for query #2.
   //
   // This ensures we're done reading textures before swapping buffers.
   WaitForPreviousPresentQuery();
 }
 
+bool
+RenderCompositorANGLE::ResizeBufferIfNeeded()
+{
+  MOZ_ASSERT(mSwapChain);
+
+  LayoutDeviceIntSize size = mWidget->GetClientSize();
+
+  // Set size to non negative.
+  size.width = std::max(size.width, 0);
+  size.height = std::max(size.height, 0);
+
+  if (mBufferSize.isSome() && mBufferSize.ref() == size) {
+    MOZ_ASSERT(mEGLSurface);
+    return true;
+  }
+
+  HRESULT hr;
+  RefPtr<ID3D11Texture2D> backBuf;
+
+  // Release EGLSurface of back buffer before calling ResizeBuffers().
+  DestroyEGLSurface();
+
+  // Reset buffer size
+  mBufferSize.reset();
+
+  // Resize swap chain
+  DXGI_SWAP_CHAIN_DESC desc;
+  hr = mSwapChain->GetDesc(&desc);
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed to read swap chain description: " << gfx::hexa(hr) << " Size : " << size;
+    return false;
+  }
+  hr = mSwapChain->ResizeBuffers(desc.BufferCount, size.width, size.height, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr) << " Size : " << size;
+    return false;
+  }
+
+  hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)getter_AddRefs(backBuf));
+  if (hr == DXGI_ERROR_INVALID_CALL) {
+    // This happens on some GPUs/drivers when there's a TDR.
+    if (mDevice->GetDeviceRemovedReason() != S_OK) {
+      gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr) << " Size : " << size;
+      return false;
+    }
+  }
+
+  const auto& egl = &gl::sEGLLibrary;
+
+  const EGLint pbuffer_attribs[]{
+    LOCAL_EGL_WIDTH, size.width,
+    LOCAL_EGL_HEIGHT, size.height,
+    LOCAL_EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, LOCAL_EGL_TRUE,
+    LOCAL_EGL_NONE};
+
+  const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
+
+  const EGLSurface surface = egl->fCreatePbufferFromClientBuffer(
+    egl->Display(), LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, mEGLConfig,
+    pbuffer_attribs);
+
+  EGLint err = egl->fGetError();
+  if (err != LOCAL_EGL_SUCCESS) {
+    gfxCriticalError() << "Failed to create Pbuffer of back buffer error: " << gfx::hexa(err) << " Size : " << size;
+    return false;
+  }
+
+  gl::GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(surface);
+
+  mEGLSurface = surface;
+  mBufferSize = Some(size);
+
+  return true;
+}
+
+void
+RenderCompositorANGLE::DestroyEGLSurface()
+{
+  const auto& egl = &gl::sEGLLibrary;
+
+  // Release EGLSurface of back buffer before calling ResizeBuffers().
+  if (mEGLSurface) {
+    gl::GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+    egl->fDestroySurface(egl->Display(), mEGLSurface);
+    mEGLSurface = nullptr;
+  }
+}
+
 void
 RenderCompositorANGLE::Pause()
 {
 }
 
 bool
 RenderCompositorANGLE::Resume()
 {
   return true;
 }
 
 LayoutDeviceIntSize
-RenderCompositorANGLE::GetClientSize()
+RenderCompositorANGLE::GetBufferSize()
 {
-  return mWidget->GetClientSize();
+  MOZ_ASSERT(mBufferSize.isSome());
+  if (mBufferSize.isNothing()) {
+    return LayoutDeviceIntSize();
+  }
+  return mBufferSize.ref();
 }
 
 void
 RenderCompositorANGLE::InsertPresentWaitQuery()
 {
   CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
   HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(mNextWaitForPresentQuery));
   if (FAILED(hr) || !mNextWaitForPresentQuery) {
--- a/gfx/webrender_bindings/RenderCompositorANGLE.h
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.h
@@ -2,21 +2,23 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef MOZILLA_GFX_RENDERCOMPOSITOR_ANGLE_H
 #define MOZILLA_GFX_RENDERCOMPOSITOR_ANGLE_H
 
+#include "mozilla/Maybe.h"
 #include "mozilla/webrender/RenderCompositor.h"
 
 struct ID3D11DeviceContext;
 struct ID3D11Device;
 struct ID3D11Query;
+struct IDXGISwapChain;
 
 namespace mozilla {
 
 namespace wr {
 
 class RenderCompositorANGLE : public RenderCompositor
 {
 public:
@@ -31,28 +33,34 @@ public:
   void EndFrame() override;
   void Pause() override;
   bool Resume() override;
 
   gl::GLContext* gl() const override { return mGL; }
 
   bool UseANGLE() const override { return true; }
 
-  LayoutDeviceIntSize GetClientSize() override;
+  LayoutDeviceIntSize GetBufferSize() override;
 
 protected:
   void InsertPresentWaitQuery();
   void WaitForPreviousPresentQuery();
+  bool ResizeBufferIfNeeded();
+  void DestroyEGLSurface();
 
   RefPtr<gl::GLContext> mGL;
+  EGLConfig mEGLConfig;
+  EGLSurface mEGLSurface;
 
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11DeviceContext> mCtx;
+  RefPtr<IDXGISwapChain> mSwapChain;
 
   RefPtr<ID3D11Query> mWaitForPresentQuery;
   RefPtr<ID3D11Query> mNextWaitForPresentQuery;
 
+  Maybe<LayoutDeviceIntSize> mBufferSize;
 };
 
 } // namespace wr
 } // namespace mozilla
 
 #endif
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -81,16 +81,16 @@ RenderCompositorOGL::Resume()
   // RenewSurface internally calls MakeCurrent.
   return mGL->RenewSurface(mWidget);
 #else
   return true;
 #endif
 }
 
 LayoutDeviceIntSize
-RenderCompositorOGL::GetClientSize()
+RenderCompositorOGL::GetBufferSize()
 {
   return mWidget->GetClientSize();
 }
 
 
 } // namespace wr
 } // namespace mozilla
--- a/gfx/webrender_bindings/RenderCompositorOGL.h
+++ b/gfx/webrender_bindings/RenderCompositorOGL.h
@@ -27,17 +27,17 @@ public:
   void EndFrame() override;
   void Pause() override;
   bool Resume() override;
 
   gl::GLContext* gl() const override { return mGL; }
 
   bool UseANGLE() const override { return false; }
 
-  LayoutDeviceIntSize GetClientSize() override;
+  LayoutDeviceIntSize GetBufferSize() override;
 
 protected:
   RefPtr<gl::GLContext> mGL;
 };
 
 } // namespace wr
 } // namespace mozilla
 
--- a/gfx/webrender_bindings/RendererOGL.cpp
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -108,17 +108,17 @@ RendererOGL::UpdateAndRender()
   // XXX set clear color if MOZ_WIDGET_ANDROID is defined.
 
   if (!mCompositor->BeginFrame()) {
     return false;
   }
 
   wr_renderer_update(mRenderer);
 
-  auto size = mCompositor->GetClientSize();
+  auto size = mCompositor->GetBufferSize();
 
   if (!wr_renderer_render(mRenderer, size.width, size.height)) {
     NotifyWebRenderError(WebRenderError::RENDER);
   }
 
   mCompositor->EndFrame();
 
   mCompositor->GetWidget()->PostRender(&widgetContext);