Implement EGL_ANGLE_direct_composition extension

On D3D11, if dcomp.dll can be loaded then EGL_ANGLE_direct_composition
is exposed. Setting EGL_DIRECT_COMPOSITION_ANGLE as an attrib on a
surface will force it to use DirectComposition to draw to the screen,
possibly saving power.

BUG=524838

Change-Id: I3ea175a97bbca1a3388ffe52fdd1587a2f0c2ce7
Reviewed-on: https://chromium-review.googlesource.com/319214
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: John Bauman <jbauman@chromium.org>
diff --git a/include/EGL/eglext.h b/include/EGL/eglext.h
index 54cfb87..bf04b37 100644
--- a/include/EGL/eglext.h
+++ b/include/EGL/eglext.h
@@ -468,6 +468,11 @@
 #define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1
 #endif /* EGL_ANGLE_surface_d3d_texture_2d_share_handle */
 
+#ifndef EGL_ANGLE_direct_composition
+#define EGL_ANGLE_direct_composition 1
+#define EGL_DIRECT_COMPOSITION_ANGLE 0x33A5
+#endif /* EGL_ANGLE_direct_composition */
+
 #ifndef EGL_ANGLE_platform_angle
 #define EGL_ANGLE_platform_angle 1
 #define EGL_PLATFORM_ANGLE_ANGLE          0x3202
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index ca291a2..fef1218 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -621,7 +621,8 @@
       glTexture3DImage(false),
       glRenderbufferImage(false),
       getAllProcAddresses(false),
-      flexibleSurfaceCompatibility(false)
+      flexibleSurfaceCompatibility(false),
+      directComposition(false)
 {
 }
 
@@ -638,6 +639,7 @@
     InsertExtensionString("EGL_ANGLE_window_fixed_size",                   windowFixedSize,                &extensionStrings);
     InsertExtensionString("EGL_ANGLE_keyed_mutex",                         keyedMutex,                     &extensionStrings);
     InsertExtensionString("EGL_ANGLE_surface_orientation",                 surfaceOrientation,             &extensionStrings);
+    InsertExtensionString("EGL_ANGLE_direct_composition",                  directComposition,              &extensionStrings);
     InsertExtensionString("EGL_NV_post_sub_buffer",                        postSubBuffer,                  &extensionStrings);
     InsertExtensionString("EGL_KHR_create_context",                        createContext,                  &extensionStrings);
     InsertExtensionString("EGL_EXT_device_query",                          deviceQuery,                    &extensionStrings);
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index ecae714..8b5cb47 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -465,6 +465,9 @@
 
     // EGL_ANGLE_flexible_surface_compatibility
     bool flexibleSurfaceCompatibility;
+
+    // EGL_ANGLE_direct_composition
+    bool directComposition;
 };
 
 struct DeviceExtensions
diff --git a/src/libANGLE/Surface.cpp b/src/libANGLE/Surface.cpp
index dc754ff..b5ed0ff 100644
--- a/src/libANGLE/Surface.cpp
+++ b/src/libANGLE/Surface.cpp
@@ -49,6 +49,8 @@
     mFlexibleSurfaceCompatibilityRequested =
         (attributes.get(EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_FALSE) == EGL_TRUE);
 
+    mDirectComposition = (attributes.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE) == EGL_TRUE);
+
     mFixedSize = (attributes.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE) == EGL_TRUE);
     if (mFixedSize)
     {
diff --git a/src/libANGLE/Surface.h b/src/libANGLE/Surface.h
index 813f2ef..e110f5d 100644
--- a/src/libANGLE/Surface.h
+++ b/src/libANGLE/Surface.h
@@ -84,6 +84,8 @@
     }
     EGLint getOrientation() const { return mOrientation; }
 
+    bool directComposition() const { return mDirectComposition; }
+
   private:
     virtual ~Surface();
     rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const override { return mImplementation; }
@@ -110,6 +112,8 @@
     size_t mFixedWidth;
     size_t mFixedHeight;
 
+    bool mDirectComposition;
+
     EGLenum mTextureFormat;
     EGLenum mTextureTarget;
 
diff --git a/src/libANGLE/renderer/d3d/DisplayD3D.cpp b/src/libANGLE/renderer/d3d/DisplayD3D.cpp
index 7c53c81..740bc76 100644
--- a/src/libANGLE/renderer/d3d/DisplayD3D.cpp
+++ b/src/libANGLE/renderer/d3d/DisplayD3D.cpp
@@ -173,6 +173,7 @@
     EGLint height = attribs.get(EGL_HEIGHT, 0);
     EGLint fixedSize = attribs.get(EGL_FIXED_SIZE_ANGLE, EGL_FALSE);
     EGLint orientation = attribs.get(EGL_SURFACE_ORIENTATION_ANGLE, 0);
+    EGLint directComposition = attribs.get(EGL_DIRECT_COMPOSITION_ANGLE, EGL_FALSE);
 
     if (!fixedSize)
     {
@@ -181,7 +182,7 @@
     }
 
     return SurfaceD3D::createFromWindow(mRenderer, mDisplay, configuration, window, fixedSize,
-                                        width, height, orientation);
+                                        directComposition, width, height, orientation);
 }
 
 SurfaceImpl *DisplayD3D::createPbufferSurface(const egl::Config *configuration,
diff --git a/src/libANGLE/renderer/d3d/SurfaceD3D.cpp b/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
index 44c525b..6a8906f 100644
--- a/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
+++ b/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
@@ -24,7 +24,8 @@
 SurfaceD3D *SurfaceD3D::createOffscreen(RendererD3D *renderer, egl::Display *display, const egl::Config *config, EGLClientBuffer shareHandle,
                                         EGLint width, EGLint height)
 {
-    return new SurfaceD3D(renderer, display, config, width, height, EGL_TRUE, 0, shareHandle, NULL);
+    return new SurfaceD3D(renderer, display, config, width, height, EGL_TRUE, 0, EGL_FALSE,
+                          shareHandle, NULL);
 }
 
 SurfaceD3D *SurfaceD3D::createFromWindow(RendererD3D *renderer,
@@ -32,12 +33,13 @@
                                          const egl::Config *config,
                                          EGLNativeWindowType window,
                                          EGLint fixedSize,
+                                         EGLint directComposition,
                                          EGLint width,
                                          EGLint height,
                                          EGLint orientation)
 {
     return new SurfaceD3D(renderer, display, config, width, height, fixedSize, orientation,
-                          static_cast<EGLClientBuffer>(0), window);
+                          directComposition, static_cast<EGLClientBuffer>(0), window);
 }
 
 SurfaceD3D::SurfaceD3D(RendererD3D *renderer,
@@ -47,6 +49,7 @@
                        EGLint height,
                        EGLint fixedSize,
                        EGLint orientation,
+                       EGLint directComposition,
                        EGLClientBuffer shareHandle,
                        EGLNativeWindowType window)
     : SurfaceImpl(),
@@ -58,7 +61,7 @@
       mDepthStencilFormat(config->depthStencilFormat),
       mSwapChain(nullptr),
       mSwapIntervalDirty(true),
-      mNativeWindow(window, config),
+      mNativeWindow(window, config, directComposition),
       mWidth(width),
       mHeight(height),
       mSwapInterval(1),
diff --git a/src/libANGLE/renderer/d3d/SurfaceD3D.h b/src/libANGLE/renderer/d3d/SurfaceD3D.h
index a2929dc..b925bfc 100644
--- a/src/libANGLE/renderer/d3d/SurfaceD3D.h
+++ b/src/libANGLE/renderer/d3d/SurfaceD3D.h
@@ -30,6 +30,7 @@
                                         const egl::Config *config,
                                         EGLNativeWindowType window,
                                         EGLint fixedSize,
+                                        EGLint directComposition,
                                         EGLint width,
                                         EGLint height,
                                         EGLint orientation);
@@ -73,6 +74,7 @@
                EGLint height,
                EGLint fixedSize,
                EGLint orientation,
+               EGLint directComposition,
                EGLClientBuffer shareHandle,
                EGLNativeWindowType window);
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h b/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h
index adaa9d9..f28ce4f 100644
--- a/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h
+++ b/src/libANGLE/renderer/d3d/d3d11/NativeWindow.h
@@ -44,14 +44,21 @@
 typedef IDXGIFactory DXGIFactory;
 #endif
 
+typedef interface IDCompositionDevice IDCompositionDevice;
+typedef interface IDCompositionTarget IDCompositionTarget;
+typedef interface IDCompositionVisual IDCompositionVisual;
+
 namespace rx
 {
 
 class NativeWindow
 {
   public:
-    explicit NativeWindow(EGLNativeWindowType window, const egl::Config *config);
+    explicit NativeWindow(EGLNativeWindowType window,
+                          const egl::Config *config,
+                          bool directComposition);
 
+    ~NativeWindow();
     bool initialize();
     bool getClientRect(LPRECT rect);
     bool isIconic();
@@ -63,11 +70,17 @@
 
     inline EGLNativeWindowType getNativeWindow() const { return mWindow; }
 
+    void commitChange();
+
   private:
     EGLNativeWindowType mWindow;
 
-#if defined(ANGLE_ENABLE_WINDOWS_STORE)
+    bool mDirectComposition;
+    IDCompositionDevice *mDevice;
+    IDCompositionTarget *mCompositionTarget;
+    IDCompositionVisual *mVisual;
     const egl::Config *mConfig;
+#if defined(ANGLE_ENABLE_WINDOWS_STORE)
     std::shared_ptr<InspectableNativeWindow> mImpl;
 #endif
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index 2db242c..c4ca8f3 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -499,6 +499,7 @@
 
     mD3d11Module = NULL;
     mDxgiModule = NULL;
+    mDCompModule          = NULL;
     mCreatedWithDeviceEXT = false;
     mEGLDevice            = nullptr;
 
@@ -765,6 +766,7 @@
             TRACE_EVENT0("gpu.angle", "Renderer11::initialize (Load DLLs)");
             mDxgiModule  = LoadLibrary(TEXT("dxgi.dll"));
             mD3d11Module = LoadLibrary(TEXT("d3d11.dll"));
+            mDCompModule = LoadLibrary(TEXT("dcomp.dll"));
 
             if (mD3d11Module == nullptr || mDxgiModule == nullptr)
             {
@@ -1089,6 +1091,7 @@
     outExtensions->glRenderbufferImage   = true;
 
     outExtensions->flexibleSurfaceCompatibility = true;
+    outExtensions->directComposition            = !!mDCompModule;
 }
 
 gl::Error Renderer11::flush()
@@ -2604,6 +2607,12 @@
         mDxgiModule = NULL;
     }
 
+    if (mDCompModule)
+    {
+        FreeLibrary(mDCompModule);
+        mDCompModule = NULL;
+    }
+
     mCompiler.release();
 }
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
index 4bfcbf3..8759f80 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -327,6 +327,7 @@
 
     HMODULE mD3d11Module;
     HMODULE mDxgiModule;
+    HMODULE mDCompModule;
     std::vector<D3D_FEATURE_LEVEL> mAvailableFeatureLevels;
     D3D_DRIVER_TYPE mDriverType;
     bool mCreatedWithDeviceEXT;
diff --git a/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
index 21ce8bb..113bcdd 100644
--- a/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
@@ -821,6 +821,8 @@
         ERR("Present failed with error code 0x%08X", result);
     }
 
+    mNativeWindow.commitChange();
+
     return EGL_SUCCESS;
 }
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp b/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp
index 4f773bb..3f477b7 100644
--- a/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow.cpp
@@ -11,14 +11,31 @@
 
 #include "common/debug.h"
 
+#include <initguid.h>
+#include <dcomp.h>
+
 namespace rx
 {
 
-NativeWindow::NativeWindow(EGLNativeWindowType window, const egl::Config *)
-    : mWindow(window)
+NativeWindow::NativeWindow(EGLNativeWindowType window,
+                           const egl::Config *config,
+                           bool directComposition)
+    : mWindow(window),
+      mDirectComposition(directComposition),
+      mCompositionTarget(nullptr),
+      mDevice(nullptr),
+      mVisual(nullptr),
+      mConfig(config)
 {
 }
 
+NativeWindow::~NativeWindow()
+{
+    SafeRelease(mCompositionTarget);
+    SafeRelease(mDevice);
+    SafeRelease(mVisual);
+}
+
 bool NativeWindow::initialize()
 {
     return true;
@@ -48,6 +65,83 @@
         return E_INVALIDARG;
     }
 
+    if (mDirectComposition)
+    {
+        HMODULE dcomp = ::GetModuleHandle(TEXT("dcomp.dll"));
+        if (!dcomp)
+        {
+            return E_INVALIDARG;
+        }
+
+        typedef HRESULT(WINAPI * PFN_DCOMPOSITION_CREATE_DEVICE)(
+            IDXGIDevice * dxgiDevice, REFIID iid, void **dcompositionDevice);
+        PFN_DCOMPOSITION_CREATE_DEVICE createDComp =
+            reinterpret_cast<PFN_DCOMPOSITION_CREATE_DEVICE>(
+                GetProcAddress(dcomp, "DCompositionCreateDevice"));
+        if (!createDComp)
+        {
+            return E_INVALIDARG;
+        }
+
+        if (!mDevice)
+        {
+            IDXGIDevice *dxgiDevice = d3d11::DynamicCastComObject<IDXGIDevice>(device);
+            HRESULT result = createDComp(dxgiDevice, __uuidof(IDCompositionDevice),
+                                         reinterpret_cast<void **>(&mDevice));
+            SafeRelease(dxgiDevice);
+
+            if (FAILED(result))
+            {
+                return result;
+            }
+        }
+
+        if (!mCompositionTarget)
+        {
+            HRESULT result = mDevice->CreateTargetForHwnd(mWindow, TRUE, &mCompositionTarget);
+            if (FAILED(result))
+            {
+                return result;
+            }
+        }
+
+        if (!mVisual)
+        {
+            HRESULT result = mDevice->CreateVisual(&mVisual);
+            if (FAILED(result))
+            {
+                return result;
+            }
+        }
+
+        IDXGIFactory2 *factory2             = d3d11::DynamicCastComObject<IDXGIFactory2>(factory);
+        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
+        swapChainDesc.Width                 = width;
+        swapChainDesc.Height                = height;
+        swapChainDesc.Format                = format;
+        swapChainDesc.Stereo                = FALSE;
+        swapChainDesc.SampleDesc.Count      = 1;
+        swapChainDesc.SampleDesc.Quality    = 0;
+        swapChainDesc.BufferUsage           = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
+        swapChainDesc.BufferCount           = 2;
+        swapChainDesc.Scaling               = DXGI_SCALING_STRETCH;
+        swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+        swapChainDesc.AlphaMode =
+            mConfig->alphaSize == 0 ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
+        swapChainDesc.Flags         = 0;
+        IDXGISwapChain1 *swapChain1 = nullptr;
+        HRESULT result =
+            factory2->CreateSwapChainForComposition(device, &swapChainDesc, nullptr, &swapChain1);
+        if (SUCCEEDED(result))
+        {
+            *swapChain = static_cast<DXGISwapChain *>(swapChain1);
+        }
+        mVisual->SetContent(swapChain1);
+        mCompositionTarget->SetRoot(mVisual);
+        SafeRelease(factory2);
+        return result;
+    }
+
     // Use IDXGIFactory2::CreateSwapChainForHwnd if DXGI 1.2 is available to create a DXGI_SWAP_EFFECT_SEQUENTIAL swap chain.
     IDXGIFactory2 *factory2 = d3d11::DynamicCastComObject<IDXGIFactory2>(factory);
     if (factory2 != nullptr)
@@ -96,4 +190,12 @@
 
     return factory->CreateSwapChain(device, &swapChainDesc, swapChain);
 }
+
+void NativeWindow::commitChange()
+{
+    if (mDevice)
+    {
+        mDevice->Commit();
+    }
+}
 }
diff --git a/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp b/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp
index 5eb1d8c..47a6dae 100644
--- a/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/winrt/InspectableNativeWindow.cpp
@@ -11,12 +11,22 @@
 
 namespace rx
 {
-NativeWindow::NativeWindow(EGLNativeWindowType window, const egl::Config *config)
+NativeWindow::NativeWindow(EGLNativeWindowType window,
+                           const egl::Config *config,
+                           bool directComposition)
 {
     mWindow = window;
     mConfig = config;
 }
 
+NativeWindow::~NativeWindow()
+{
+}
+
+void NativeWindow::commitChange()
+{
+}
+
 bool NativeWindow::initialize()
 {
     // If the native window type is a IPropertySet, extract the
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index a6d2313..67d1edc 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -385,6 +385,13 @@
           case EGL_VG_ALPHA_FORMAT:
             return Error(EGL_BAD_MATCH);
 
+          case EGL_DIRECT_COMPOSITION_ANGLE:
+              if (!displayExtensions.directComposition)
+              {
+                  return Error(EGL_BAD_ATTRIBUTE);
+              }
+              break;
+
           default:
             return Error(EGL_BAD_ATTRIBUTE);
         }
diff --git a/src/libGLESv2/entry_points_egl.cpp b/src/libGLESv2/entry_points_egl.cpp
index b67abe2..2b04611 100644
--- a/src/libGLESv2/entry_points_egl.cpp
+++ b/src/libGLESv2/entry_points_egl.cpp
@@ -447,6 +447,16 @@
           }
           *value = eglSurface->getOrientation();
           break;
+      case EGL_DIRECT_COMPOSITION_ANGLE:
+          if (!display->getExtensions().directComposition)
+          {
+              SetGlobalError(Error(EGL_BAD_ATTRIBUTE,
+                                   "EGL_DIRECT_COMPOSITION_ANGLE cannot be used without "
+                                   "EGL_ANGLE_direct_composition support."));
+              return EGL_FALSE;
+          }
+          *value = eglSurface->directComposition();
+          break;
       default:
         SetGlobalError(Error(EGL_BAD_ATTRIBUTE));
         return EGL_FALSE;