b=618892; [webgl] use ANGLE pbuffers to speed up webgl with d3d10 layers; r=bas
authorVladimir Vukicevic <vladimir@pobox.com>
Thu, 06 Jan 2011 14:07:12 -0800
changeset 60089 214726475a6bc8c2f179cb1e459990153f05e53f
parent 60088 83e84e7592c03bcdcc06d2bff38e7622e21cb464
child 60090 6f1e8aa5fc804e74ede91a8a1a22f4f0b7016f66
push idunknown
push userunknown
push dateunknown
reviewersbas
bugs618892
milestone2.0b9pre
b=618892; [webgl] use ANGLE pbuffers to speed up webgl with d3d10 layers; r=bas
gfx/layers/d3d10/CanvasLayerD3D10.cpp
gfx/layers/d3d10/CanvasLayerD3D10.h
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/thebes/GLContext.h
gfx/thebes/GLContextProviderEGL.cpp
--- a/gfx/layers/d3d10/CanvasLayerD3D10.cpp
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.cpp
@@ -71,44 +71,64 @@ CanvasLayerD3D10::Initialize(const Data&
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 
   if (mSurface && mSurface->GetType() == gfxASurface::SurfaceTypeD2D) {
     void *data = mSurface->GetData(&gKeyD3D10Texture);
     if (data) {
       mTexture = static_cast<ID3D10Texture2D*>(data);
-      mIsD2DTexture = true;
+      mIsD2DTexture = PR_TRUE;
       device()->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView));
       return;
     }
   }
 
-  mIsD2DTexture = false;
+  mIsD2DTexture = PR_FALSE;
+  mUsingSharedTexture = PR_FALSE;
 
-  CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, mBounds.width, mBounds.height, 1, 1);
-  desc.Usage = D3D10_USAGE_DYNAMIC;
-  desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
+  HANDLE shareHandle = mGLContext ? mGLContext->GetD3DShareHandle() : nsnull;
+  if (shareHandle) {
+    HRESULT hr = device()->OpenSharedResource(shareHandle, __uuidof(ID3D10Texture2D), getter_AddRefs(mTexture));
+    if (SUCCEEDED(hr))
+      mUsingSharedTexture = PR_TRUE;
+  }
 
-  HRESULT hr = device()->CreateTexture2D(&desc, NULL, getter_AddRefs(mTexture));
-  if (FAILED(hr)) {
-    NS_WARNING("Failed to create texture for CanvasLayer!");
-    return;
+  if (!mUsingSharedTexture) {
+    CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, mBounds.width, mBounds.height, 1, 1);
+    desc.Usage = D3D10_USAGE_DYNAMIC;
+    desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
+
+    HRESULT hr = device()->CreateTexture2D(&desc, NULL, getter_AddRefs(mTexture));
+    if (FAILED(hr)) {
+      NS_WARNING("Failed to create texture for CanvasLayer!");
+      return;
+    }
   }
+
   device()->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView));
 }
 
 void
 CanvasLayerD3D10::Updated(const nsIntRect& aRect)
 {
   if (mIsD2DTexture) {
     mSurface->Flush();
     return;
   }
 
+  if (mUsingSharedTexture) {
+    // need to sync on the d3d9 device
+    if (mGLContext) {
+      mGLContext->MakeCurrent();
+      mGLContext->fFinish();
+    }
+    return;
+  }
+
   if (mGLContext) {
     // WebGL reads entire surface.
     D3D10_MAPPED_TEXTURE2D map;
     
     HRESULT hr = mTexture->Map(0, D3D10_MAP_WRITE_DISCARD, 0, &map);
 
     if (FAILED(hr)) {
       NS_WARNING("Failed to map CanvasLayer texture.");
--- a/gfx/layers/d3d10/CanvasLayerD3D10.h
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.h
@@ -77,13 +77,14 @@ private:
   nsRefPtr<ID3D10Texture2D> mTexture;
   nsRefPtr<ID3D10ShaderResourceView> mSRView;
 
   PRUint32 mCanvasFramebuffer;
 
   PRPackedBool mDataIsPremultiplied;
   PRPackedBool mNeedsYFlip;
   PRPackedBool mIsD2DTexture;
+  PRPackedBool mUsingSharedTexture;
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_CANVASLAYERD3D10_H */
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -87,23 +87,21 @@ LayerManagerD3D10::~LayerManagerD3D10()
   Destroy();
 }
 
 bool
 LayerManagerD3D10::Initialize()
 {
   HRESULT hr;
 
-  cairo_device_t *device = gfxWindowsPlatform::GetPlatform()->GetD2DDevice();
-  if (!device) {
-    return false;
+  mDevice = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
+  if (!mDevice) {
+      return false;
   }
 
-  mDevice = cairo_d2d_device_get_device(device);
-
   UINT size = sizeof(ID3D10Effect*);
   if (FAILED(mDevice->GetPrivateData(sEffect, &size, mEffect.StartAssignment()))) {
     D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc)
 	GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateEffectFromMemory");
 
     if (!createEffect) {
       return false;
     }
--- a/gfx/thebes/GLContext.h
+++ b/gfx/thebes/GLContext.h
@@ -491,16 +491,21 @@ public:
 
     PRBool IsGlobalSharedContext() { return mIsGlobalSharedContext; }
     void SetIsGlobalSharedContext(PRBool aIsOne) { mIsGlobalSharedContext = aIsOne; }
 
     const ContextFormat& CreationFormat() { return mCreationFormat; }
     const ContextFormat& ActualFormat() { return mActualFormat; }
 
     /**
+     * If this GL context has a D3D texture share handle, returns non-null.
+     */
+    virtual void *GetD3DShareHandle() { return nsnull; }
+
+    /**
      * If this context is double-buffered, returns TRUE.
      */
     virtual PRBool IsDoubleBuffered() { return PR_FALSE; }
 
     /**
      * If this context is the GLES2 API, returns TRUE.
      * This means that various GLES2 restrictions might be in effect (modulo
      * extensions).
--- a/gfx/thebes/GLContextProviderEGL.cpp
+++ b/gfx/thebes/GLContextProviderEGL.cpp
@@ -204,20 +204,22 @@ is_power_of_two(int v)
 
 static class EGLLibrary
 {
 public:
     EGLLibrary() 
         : mInitialized(PR_FALSE),
           mEGLLibrary(nsnull)
     {
+        mIsANGLE = PR_FALSE;
         mHave_EGL_KHR_image_base = PR_FALSE;
         mHave_EGL_KHR_image_pixmap = PR_FALSE;
         mHave_EGL_KHR_gl_texture_2D_image = PR_FALSE;
         mHave_EGL_KHR_lock_surface = PR_FALSE;
+        mHave_EGL_ANGLE_surface_d3d_share_handle = PR_FALSE;
     }
 
     typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void *display_id);
     pfnGetDisplay fGetDisplay;
     typedef EGLContext (GLAPIENTRY * pfnGetCurrentContext)(void);
     pfnGetCurrentContext fGetCurrentContext;
     typedef EGLBoolean (GLAPIENTRY * pfnMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
     pfnMakeCurrent fMakeCurrent;
@@ -271,16 +273,19 @@ public:
     // New extension which allow us to lock texture and get raw image pointer
     typedef EGLBoolean (GLAPIENTRY * pfnLockSurfaceKHR)(EGLDisplay dpy, EGLSurface surface, const EGLint *attrib_list);
     pfnLockSurfaceKHR fLockSurfaceKHR;
     typedef EGLBoolean (GLAPIENTRY * pfnUnlockSurfaceKHR)(EGLDisplay dpy, EGLSurface surface);
     pfnUnlockSurfaceKHR fUnlockSurfaceKHR;
     typedef EGLBoolean (GLAPIENTRY * pfnQuerySurface)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
     pfnQuerySurface fQuerySurface;
 
+    typedef EGLBoolean (GLAPIENTRY * pfnQuerySurfacePointerANGLE)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void **value);
+    pfnQuerySurfacePointerANGLE fQuerySurfacePointerANGLE;
+
     // This is EGL specific GL ext symbol "glEGLImageTargetTexture2DOES"
     // Lets keep it here for now.
     typedef void (GLAPIENTRY * pfnImageTargetTexture2DOES)(GLenum target, GLeglImageOES image);
     pfnImageTargetTexture2DOES fImageTargetTexture2DOES;
 
     PRBool EnsureInitialized()
     {
         if (mInitialized) {
@@ -367,16 +372,21 @@ public:
         if (!LibrarySymbolLoader::LoadSymbols(mEGLLibrary, &earlySymbols[0])) {
             NS_WARNING("Couldn't find required entry points in EGL library (early init)");
             return PR_FALSE;
         }
 
         mEGLDisplay = fGetDisplay(EGL_DEFAULT_DISPLAY);
         if (!fInitialize(mEGLDisplay, NULL, NULL))
             return PR_FALSE;
+
+        const char *vendor = (const char*) fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
+        if (vendor && strstr(vendor, "TransGaming") != 0) {
+            mIsANGLE = PR_TRUE;
+        }
         
         const char *extensions = (const char*) fQueryString(mEGLDisplay, LOCAL_EGL_EXTENSIONS);
         if (!extensions)
             extensions = "";
 
         printf_stderr("Extensions: %s 0x%02x\n", extensions, extensions[0]);
         printf_stderr("Extensions length: %d\n", strlen(extensions));
 
@@ -443,40 +453,61 @@ public:
             mHave_EGL_KHR_image_pixmap = PR_FALSE;
             mHave_EGL_KHR_gl_texture_2D_image = PR_FALSE;
         }
 
         if (!fImageTargetTexture2DOES) {
             mHave_EGL_KHR_gl_texture_2D_image = PR_FALSE;
         }
 
+        if (strstr(extensions, "EGL_ANGLE_surface_d3d_share_handle")) {
+            LibrarySymbolLoader::SymLoadStruct d3dSymbols[] = {
+                { (PRFuncPtr*) &fQuerySurfacePointerANGLE, { "eglQuerySurfacePointerANGLE", NULL } },
+                { NULL, { NULL } }
+            };
+
+            LibrarySymbolLoader::LoadSymbols(mEGLLibrary, &d3dSymbols[0],
+                                             (LibrarySymbolLoader::PlatformLookupFunction)fGetProcAddress);
+            if (fQuerySurfacePointerANGLE) {
+                mHave_EGL_ANGLE_surface_d3d_share_handle = PR_TRUE;
+            }
+        }
+
         mInitialized = PR_TRUE;
         return PR_TRUE;
     }
 
     EGLDisplay Display() {
         return mEGLDisplay;
     }
 
+    PRBool IsANGLE() {
+        return mIsANGLE;
+    }
+
     PRBool HasKHRImageBase() {
         return mHave_EGL_KHR_image_base;
     }
 
     PRBool HasKHRImagePixmap() {
         return mHave_EGL_KHR_image_base;
     }
 
     PRBool HasKHRImageTexture2D() {
         return mHave_EGL_KHR_gl_texture_2D_image;
     }
 
     PRBool HasKHRLockSurface() {
         return mHave_EGL_KHR_lock_surface;
     }
 
+    PRBool HasANGLESurfaceD3DShareHandle() {
+        return mHave_EGL_ANGLE_surface_d3d_share_handle;
+    }
+
     void
     DumpEGLConfig(EGLConfig cfg)
     {
         int attrval;
         int err;
 
 #define ATTR(_x) do {                                                   \
             fGetConfigAttrib(mEGLDisplay, cfg, LOCAL_EGL_##_x, &attrval);  \
@@ -526,20 +557,23 @@ public:
 #undef ATTR
     }
 
 private:
     PRBool mInitialized;
     PRLibrary *mEGLLibrary;
     EGLDisplay mEGLDisplay;
 
+    PRPackedBool mIsANGLE;
+
     PRPackedBool mHave_EGL_KHR_image_base;
     PRPackedBool mHave_EGL_KHR_image_pixmap;
     PRPackedBool mHave_EGL_KHR_gl_texture_2D_image;
     PRPackedBool mHave_EGL_KHR_lock_surface;
+    PRPackedBool mHave_EGL_ANGLE_surface_d3d_share_handle;
 } sEGLLibrary;
 
 class GLContextEGL : public GLContext
 {
     friend class TextureImageEGL;
 
 public:
     GLContextEGL(const ContextFormat& aFormat,
@@ -551,19 +585,16 @@ public:
         : GLContext(aFormat, aIsOffscreen, aShareContext)
         , mConfig(aConfig) 
         , mSurface(aSurface), mContext(aContext)
         , mGLWidget(nsnull)
         , mThebesSurface(nsnull)
         , mBound(PR_FALSE)
         , mIsPBuffer(PR_FALSE)
         , mIsDoubleBuffered(PR_FALSE)
-#ifdef XP_WIN
-        , mWnd(0)
-#endif
     {
         // any EGL contexts will always be GLESv2
         SetIsGLES2(PR_TRUE);
 
 #ifdef DEBUG
         printf_stderr("Initializing context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
 #endif
     }
@@ -752,48 +783,55 @@ public:
     static already_AddRefed<GLContextEGL>
     CreateEGLPixmapOffscreenContext(const gfxIntSize& aSize,
                                     const ContextFormat& aFormat);
 
     static already_AddRefed<GLContextEGL>
     CreateEGLPBufferOffscreenContext(const gfxIntSize& aSize,
                                      const ContextFormat& aFormat);
 
-#ifdef XP_WIN
-    static already_AddRefed<GLContextEGL>
-    CreateEGLWin32OffscreenContext(const gfxIntSize& aSize,
-                                   const ContextFormat& aFormat);
-
-    void HoldWin32Window(HWND aWnd) { mWnd = aWnd; }
-    HWND GetWin32Window() { return mWnd; }
-#endif
-
     void SetOffscreenSize(const gfxIntSize &aRequestedSize,
                           const gfxIntSize &aActualSize)
     {
         mOffscreenSize = aRequestedSize;
         mOffscreenActualSize = aActualSize;
     }
 
+    void *GetD3DShareHandle() {
+        if (!sEGLLibrary.HasANGLESurfaceD3DShareHandle()) {
+            return nsnull;
+        }
+
+        void *h = nsnull;
+
+#ifndef EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE
+#define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
+#endif
+
+        if (!sEGLLibrary.fQuerySurfacePointerANGLE(EGL_DISPLAY(), mSurface,
+                                                   EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, (void**) &h))
+        {
+            return nsnull;
+        }
+
+        return h;
+    }
+
 protected:
     friend class GLContextProviderEGL;
 
     EGLConfig  mConfig;
     EGLSurface mSurface;
     EGLContext mContext;
     void *mGLWidget;
     nsRefPtr<gfxASurface> mThebesSurface;
     PRBool mBound;
 
     PRPackedBool mIsPBuffer;
     PRPackedBool mIsDoubleBuffered;
-
-#ifdef XP_WIN
-    AutoDestroyHWND mWnd;
-#endif
 };
 
 PRBool
 GLContextEGL::BindTex2DOffscreen(GLContext *aOffscreen)
 {
     if (aOffscreen->GetContextType() != ContextTypeEGL) {
         NS_WARNING("non-EGL context");
         return PR_FALSE;
@@ -1654,24 +1692,33 @@ GLContextEGL::CreateEGLPBufferOffscreenC
 
         LOCAL_EGL_RED_SIZE, aFormat.red,
         LOCAL_EGL_GREEN_SIZE, aFormat.green,
         LOCAL_EGL_BLUE_SIZE, aFormat.blue,
         LOCAL_EGL_ALPHA_SIZE, aFormat.alpha,
         LOCAL_EGL_DEPTH_SIZE, aFormat.minDepth,
         LOCAL_EGL_STENCIL_SIZE, aFormat.minStencil,
 
+        // these get overwritten below; if you add anything above
         aFormat.minAlpha ?
           LOCAL_EGL_BIND_TO_TEXTURE_RGBA :
           LOCAL_EGL_BIND_TO_TEXTURE_RGB,
         LOCAL_EGL_TRUE,
 
         LOCAL_EGL_NONE
     };
 
+    // if we're running under ANGLE, we can't set
+    // BIND_TO_TEXTURE since we're probably doing d3d interop
+    if (sEGLLibrary.IsANGLE()) {
+        int k = sizeof(attribs)/sizeof(EGLint) - 3;
+        attribs[k] = LOCAL_EGL_NONE;
+        attribs[k+1] = LOCAL_EGL_NONE;
+    }
+
     EGLConfig configs[64];
     int numConfigs = 64;
 
     if (!sEGLLibrary.fChooseConfig(EGL_DISPLAY(),
                                    attribs,
                                    configs, numConfigs,
                                    &numConfigs)
         || numConfigs == 0)
@@ -1689,25 +1736,32 @@ GLContextEGL::CreateEGLPBufferOffscreenC
 
     gfxIntSize pbsize(aSize);
 
     EGLint pbattrs[] = {
         LOCAL_EGL_WIDTH, 0,
         LOCAL_EGL_HEIGHT, 0,
 
         LOCAL_EGL_TEXTURE_TARGET, LOCAL_EGL_TEXTURE_2D,
-
         LOCAL_EGL_TEXTURE_FORMAT,
         aFormat.minAlpha ?
           LOCAL_EGL_TEXTURE_RGBA :
           LOCAL_EGL_TEXTURE_RGB,
 
         LOCAL_EGL_NONE
     };
 
+    // if under ANGLE, then TEXTURE_TARGET/TEXTURE_FORMAT
+    // need to be stripped out (we don't support bind-to-texture,
+    // and just do d3d interop)
+    if (sEGLLibrary.IsANGLE()) {
+        pbattrs[4] = LOCAL_EGL_NONE;
+        pbattrs[5] = LOCAL_EGL_NONE;
+    }
+
 TRY_AGAIN_POWER_OF_TWO:
     pbattrs[1] = pbsize.width;
     pbattrs[3] = pbsize.height;
 
     surface = sEGLLibrary.fCreatePbufferSurface(EGL_DISPLAY(), config, pbattrs);
     if (!surface) {
         if (!is_power_of_two(pbsize.width) ||
             !is_power_of_two(pbsize.height))
@@ -1897,123 +1951,32 @@ GLContextEGL::CreateEGLPixmapOffscreenCo
         !glContext->ResizeOffscreenFBO(aSize))
         return nsnull;
 
     glContext->HoldSurface(thebesSurface);
 
     return glContext.forget();
 }
 
-#ifdef XP_WIN
-already_AddRefed<GLContextEGL>
-GLContextEGL::CreateEGLWin32OffscreenContext(const gfxIntSize& aSize,
-                                             const ContextFormat& aFormat)
-{
-    if (!sEGLLibrary.EnsureInitialized()) {
-        return nsnull;
-    }
-
-    WNDCLASSW wc;
-    if (!GetClassInfoW(GetModuleHandle(NULL), L"ANGLEContextClass", &wc)) {
-        ZeroMemory(&wc, sizeof(WNDCLASSW));
-        wc.style = CS_OWNDC;
-        wc.hInstance = GetModuleHandle(NULL);
-        wc.lpfnWndProc = DefWindowProc;
-        wc.lpszClassName = L"ANGLEContextClass";
-        if (!RegisterClassW(&wc)) {
-            NS_WARNING("Failed to register ANGLEContextClass?!");
-            return NULL;
-        }
-    }
-
-    AutoDestroyHWND wnd = CreateWindowW(L"ANGLEContextClass", L"ANGLEContext", 0,
-                                        0, 0, 16, 16,
-                                        NULL, NULL, GetModuleHandle(NULL), NULL);
-    NS_ENSURE_TRUE(HWND(wnd), NULL);
-
-    EGLConfig  config;
-    EGLSurface surface;
-    EGLContext context;
-
-    // We don't really care, we're going to use a FBO anyway
-    EGLint attribs[] = {
-        LOCAL_EGL_SURFACE_TYPE,    LOCAL_EGL_WINDOW_BIT,
-        LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
-        LOCAL_EGL_NONE
-    };
-
-    EGLint ncfg = 1;
-    if (!sEGLLibrary.fChooseConfig(sEGLLibrary.Display(), attribs, &config, ncfg, &ncfg) ||
-        ncfg < 1)
-    {
-        return nsnull;
-    }
-
-    surface = sEGLLibrary.fCreateWindowSurface(sEGLLibrary.Display(),
-                                               config,
-                                               HWND(wnd),
-                                               0);
-    if (!surface) {
-        return nsnull;
-    }
-
-    if (!sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API)) {
-        sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
-        return nsnull;
-    }
-
-    EGLint cxattribs[] = {
-        LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2,
-        LOCAL_EGL_NONE
-    };
-    context = sEGLLibrary.fCreateContext(sEGLLibrary.Display(),
-                                         config,
-                                         EGL_NO_CONTEXT,
-                                         cxattribs);
-    if (!context) {
-        sEGLLibrary.fDestroySurface(sEGLLibrary.Display(), surface);
-        return nsnull;
-    }
-
-    nsRefPtr<GLContextEGL> glContext = new GLContextEGL(aFormat, nsnull,
-                                                        config, surface, context,
-                                                        PR_TRUE);
-
-    // hold this even before we initialize, because we need to make
-    // sure it gets destroyed after the surface etc. in case of error.
-    glContext->HoldWin32Window(wnd.forget());
-
-    if (!glContext->Init() ||
-        !glContext->ResizeOffscreenFBO(aSize))
-    {
-        return nsnull;
-    }
-
-    return glContext.forget();
-}
-#endif
-
 // Under EGL, if we're under X11, then we have to create a Pixmap
 // because Maemo's EGL implementation doesn't support pbuffers at all
 // for some reason.  On Android, pbuffers are supported fine, though
 // often without the ability to texture from them directly.
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateOffscreen(const gfxIntSize& aSize,
                                       const ContextFormat& aFormat)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
         return nsnull;
     }
 
-#if defined(ANDROID)
+#if defined(ANDROID) || defined(XP_WIN)
     return GLContextEGL::CreateEGLPBufferOffscreenContext(aSize, aFormat);
 #elif defined(MOZ_X11)
     return GLContextEGL::CreateEGLPixmapOffscreenContext(aSize, aFormat);
-#elif defined(XP_WIN)
-    return GLContextEGL::CreateEGLWin32OffscreenContext(aSize, aFormat);
 #else
     return nsnull;
 #endif
 }
 
 static ContextFormat
 ContentTypeToGLFormat(gfxASurface::gfxContentType aCType)
 {