Issue=91 -- implement pbuffers in ANGLE; add D3D share handle support

git-svn-id: https://angleproject.googlecode.com/svn/trunk@558 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/include/EGL/eglext.h b/include/EGL/eglext.h
index 638e336..b650a50 100644
--- a/include/EGL/eglext.h
+++ b/include/EGL/eglext.h
@@ -215,6 +215,19 @@
 typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBNVPROC) (EGLSyncNV sync, EGLint attribute, EGLint *value);
 #endif
 
+#ifndef EGL_ANGLE_query_surface_pointer
+#define EGL_ANGLE_query_surface_pointer 1
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean eglQuerySurfacePointerANGLE(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void **value);
+#endif
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSURFACEPOINTERANGLEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, void **value);
+#endif
+
+#ifndef EGL_ANGLE_surface_d3d_texture_2d_share_handle
+#define EGL_ANGLE_surface_d3d_texture_2d_share_handle
+#define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/libEGL/Config.cpp b/src/libEGL/Config.cpp
index 284f61d..511b8ae 100644
--- a/src/libEGL/Config.cpp
+++ b/src/libEGL/Config.cpp
@@ -19,10 +19,10 @@
 
 namespace egl
 {
-Config::Config(D3DDISPLAYMODE displayMode, EGLint minInterval, EGLint maxInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample)
+Config::Config(D3DDISPLAYMODE displayMode, EGLint minInterval, EGLint maxInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample, EGLint texWidth, EGLint texHeight)
     : mDisplayMode(displayMode), mRenderTargetFormat(renderTargetFormat), mDepthStencilFormat(depthStencilFormat), mMultiSample(multiSample)
 {
-    set(displayMode, minInterval, maxInterval, renderTargetFormat, depthStencilFormat, multiSample);
+    set(displayMode, minInterval, maxInterval, renderTargetFormat, depthStencilFormat, multiSample, texWidth, texHeight);
 }
 
 void Config::setDefaults()
@@ -62,7 +62,7 @@
     mTransparentBlueValue = EGL_DONT_CARE;
 }
 
-void Config::set(D3DDISPLAYMODE displayMode, EGLint minInterval, EGLint maxInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample)
+void Config::set(D3DDISPLAYMODE displayMode, EGLint minInterval, EGLint maxInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample, EGLint texWidth, EGLint texHeight)
 {
     switch (renderTargetFormat)
     {
@@ -158,9 +158,9 @@
 
     mLevel = 0;
     mMatchNativePixmap = EGL_NONE;
-    mMaxPBufferWidth = 0;
-    mMaxPBufferHeight = 0;
-    mMaxPBufferPixels = 0;
+    mMaxPBufferWidth = texWidth;
+    mMaxPBufferHeight = texHeight;
+    mMaxPBufferPixels = texWidth*texHeight;
     mMaxSwapInterval = maxInterval;
     mMinSwapInterval = minInterval;
     mNativeRenderable = EGL_FALSE;
@@ -282,9 +282,9 @@
 {
 }
 
-void ConfigSet::add(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample)
+void ConfigSet::add(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample, EGLint texWidth, EGLint texHeight)
 {
-    Config config(displayMode, minSwapInterval, maxSwapInterval, renderTargetFormat, depthStencilFormat, multiSample);
+    Config config(displayMode, minSwapInterval, maxSwapInterval, renderTargetFormat, depthStencilFormat, multiSample, texWidth, texHeight);
 
     mSet.insert(config);
 }
@@ -337,6 +337,9 @@
               case EGL_RENDERABLE_TYPE:           match = (config->mRenderableType & attribute[1]) == attribute[1]; break;
               case EGL_MATCH_NATIVE_PIXMAP:       match = false; UNIMPLEMENTED();                                   break;
               case EGL_CONFORMANT:                match = (config->mConformant & attribute[1]) == attribute[1];     break;
+              case EGL_MAX_PBUFFER_WIDTH:         match = config->mMaxPBufferWidth >= attribute[1];                 break;
+              case EGL_MAX_PBUFFER_HEIGHT:        match = config->mMaxPBufferHeight >= attribute[1];                break;
+              case EGL_MAX_PBUFFER_PIXELS:        match = config->mMaxPBufferPixels >= attribute[1];                break;
               default:
                 return false;
             }
diff --git a/src/libEGL/Config.h b/src/libEGL/Config.h
index b340f56..95626ed 100644
--- a/src/libEGL/Config.h
+++ b/src/libEGL/Config.h
@@ -26,10 +26,10 @@
 class Config
 {
   public:
-    Config(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample);
+    Config(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample, EGLint texWidth, EGLint texHeight);
 
     void setDefaults();
-    void set(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample);
+    void set(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample, EGLint texWidth, EGLint texHeight);
     EGLConfig getHandle() const;
 
     const D3DDISPLAYMODE mDisplayMode;
@@ -99,7 +99,7 @@
   public:
     ConfigSet();
 
-    void add(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample);
+    void add(D3DDISPLAYMODE displayMode, EGLint minSwapInterval, EGLint maxSwapInterval, D3DFORMAT renderTargetFormat, D3DFORMAT depthStencilFormat, EGLint multiSample, EGLint texWidth, EGLint texHeight);
     size_t size() const;
     bool getConfigs(EGLConfig *configs, const EGLint *attribList, EGLint configSize, EGLint *numConfig);
     const egl::Config *get(EGLConfig configHandle);
diff --git a/src/libEGL/Display.cpp b/src/libEGL/Display.cpp
index 4673e73..15901ae 100644
--- a/src/libEGL/Display.cpp
+++ b/src/libEGL/Display.cpp
@@ -181,7 +181,8 @@
                         {
                             // FIXME: enumerate multi-sampling
 
-                            configSet.add(currentDisplayMode, mMinSwapInterval, mMaxSwapInterval, renderTargetFormat, depthStencilFormat, 0);
+                            configSet.add(currentDisplayMode, mMinSwapInterval, mMaxSwapInterval, renderTargetFormat, depthStencilFormat, 0,
+                                          mDeviceCaps.MaxTextureWidth, mDeviceCaps.MaxTextureHeight);
                         }
                     }
                 }
@@ -207,6 +208,8 @@
         return false;
     }
 
+    initExtensionString();
+
     static const TCHAR windowName[] = TEXT("AngleHiddenWindow");
     static const TCHAR className[] = TEXT("STATIC");
 
@@ -329,6 +332,9 @@
       case EGL_RENDERABLE_TYPE:           *value = configuration->mRenderableType;         break;
       case EGL_MATCH_NATIVE_PIXMAP:       *value = false; UNIMPLEMENTED();                 break;
       case EGL_CONFORMANT:                *value = configuration->mConformant;             break;
+      case EGL_MAX_PBUFFER_WIDTH:         *value = configuration->mMaxPBufferWidth;        break;
+      case EGL_MAX_PBUFFER_HEIGHT:        *value = configuration->mMaxPBufferHeight;       break;
+      case EGL_MAX_PBUFFER_PIXELS:        *value = configuration->mMaxPBufferPixels;       break;
       default:
         return false;
     }
@@ -406,6 +412,16 @@
     return surface;
 }
 
+Surface *Display::createOffscreenSurface(int width, int height, EGLConfig config)
+{
+    const Config *configuration = mConfigSet.get(config);
+
+    Surface *surface = new Surface(this, configuration, width, height);
+    mSurfaceSet.insert(surface);
+
+    return surface;
+}
+
 EGLContext Display::createContext(EGLConfig configHandle, const gl::Context *shareContext)
 {
     if (!mDevice)
@@ -671,4 +687,25 @@
 
     return presentParameters;
 }
-}
\ No newline at end of file
+
+void Display::initExtensionString()
+{
+    mExtensionString += "EGL_ANGLE_query_surface_pointer ";
+
+    if (isD3d9ExDevice()) {
+        mExtensionString += "EGL_ANGLE_surface_d3d_texture_2d_share_handle ";
+    }
+
+    std::string::size_type end = mExtensionString.find_last_not_of(' ');
+    if (end != std::string::npos)
+    {
+        mExtensionString.resize(end+1);
+    }
+}
+
+const char *Display::getExtensionString() const
+{
+    return mExtensionString.c_str();
+}
+
+}
diff --git a/src/libEGL/Display.h b/src/libEGL/Display.h
index c87d5ec..8434973 100644
--- a/src/libEGL/Display.h
+++ b/src/libEGL/Display.h
@@ -43,6 +43,7 @@
     bool getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value);
 
     egl::Surface *createWindowSurface(HWND window, EGLConfig config);
+    egl::Surface *createOffscreenSurface(int width, int height, EGLConfig config);
     EGLContext createContext(EGLConfig configHandle, const gl::Context *shareContext);
 
     void destroySurface(egl::Surface *surface);
@@ -69,6 +70,9 @@
     virtual bool getLuminanceAlphaTextureSupport();
     virtual D3DPOOL getBufferPool(DWORD usage) const;
 
+    bool isD3d9ExDevice() { return mD3d9Ex != NULL; }
+    const char *getExtensionString() const;
+
   private:
     DISALLOW_COPY_AND_ASSIGN(Display);
 
@@ -101,6 +105,9 @@
 
     bool createDevice();
     bool resetDevice();
+
+    void initExtensionString();
+    std::string mExtensionString;
 };
 }
 
diff --git a/src/libEGL/Surface.cpp b/src/libEGL/Surface.cpp
index 5e7f97a..6c55e88 100644
--- a/src/libEGL/Surface.cpp
+++ b/src/libEGL/Surface.cpp
@@ -25,6 +25,8 @@
     mSwapChain = NULL;
     mDepthStencil = NULL;
     mRenderTarget = NULL;
+    mOffscreenTexture = NULL;
+    mShareHandle = NULL;
 
     mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
     mRenderBuffer = EGL_BACK_BUFFER;
@@ -36,6 +38,25 @@
     resetSwapChain();
 }
 
+Surface::Surface(Display *display, const Config *config, EGLint width, EGLint height)
+    : mDisplay(display), mWindow(NULL), mConfig(config), mWidth(width), mHeight(height)
+{
+    mSwapChain = NULL;
+    mDepthStencil = NULL;
+    mRenderTarget = NULL;
+    mOffscreenTexture = NULL;
+    mShareHandle = NULL;
+    mWindowSubclassed = false;
+
+    mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
+    mRenderBuffer = EGL_BACK_BUFFER;
+    mSwapBehavior = EGL_BUFFER_PRESERVED;
+    mSwapInterval = -1;
+    setSwapInterval(1);
+
+    resetSwapChain(width, height);
+}
+
 Surface::~Surface()
 {
     unsubclassWindow();
@@ -61,10 +82,21 @@
         mRenderTarget->Release();
         mRenderTarget = NULL;
     }
+
+    if (mOffscreenTexture)
+    {
+        mOffscreenTexture->Release();
+        mOffscreenTexture = NULL;
+    }
 }
 
 void Surface::resetSwapChain()
 {
+    if (!mWindow) {
+        resetSwapChain(mWidth, mHeight);
+        return;
+    }
+
     RECT windowRect;
     if (!GetClientRect(getWindowHandle(), &windowRect))
     {
@@ -90,8 +122,9 @@
     // before reallocating them to free up as much video memory as possible.
     device->EvictManagedResources();
     release();
-    
+
     D3DPRESENT_PARAMETERS presentParameters = {0};
+    HRESULT result;
 
     presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
     presentParameters.BackBufferCount = 1;
@@ -107,13 +140,24 @@
     presentParameters.BackBufferWidth = backbufferWidth;
     presentParameters.BackBufferHeight = backbufferHeight;
 
-    HRESULT result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
+    if (mWindow)
+    {
+        result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
+    } else {
+        HANDLE *pShareHandle = NULL;
+        if (mDisplay->isD3d9ExDevice()) {
+            pShareHandle = &mShareHandle;
+        }
+
+        result = device->CreateTexture(presentParameters.BackBufferWidth, presentParameters.BackBufferHeight, 1, D3DUSAGE_RENDERTARGET,
+                                       presentParameters.BackBufferFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, pShareHandle);
+    }
 
     if (FAILED(result))
     {
         ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
 
-        ERR("Could not create additional swap chains: %08lX", result);
+        ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
         release();
         return error(EGL_BAD_ALLOC);
     }
@@ -131,13 +175,17 @@
         return error(EGL_BAD_ALLOC);
     }
 
-    mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mRenderTarget);
+    if (mWindow) {
+        mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mRenderTarget);
+        InvalidateRect(mWindow, NULL, FALSE);
+    } else {
+        mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
+    }
+
     mWidth = presentParameters.BackBufferWidth;
     mHeight = presentParameters.BackBufferHeight;
 
     mPresentIntervalDirty = false;
-
-    InvalidateRect(mWindow, NULL, FALSE);
 }
 
 HWND Surface::getWindowHandle()
@@ -162,6 +210,9 @@
 
 void Surface::subclassWindow()
 {
+  if (!mWindow)
+    return;
+
   SetLastError(0);
   LONG oldWndProc = SetWindowLong(mWindow, GWL_WNDPROC, reinterpret_cast<LONG>(SurfaceWindowProc));
   if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS) {
diff --git a/src/libEGL/Surface.h b/src/libEGL/Surface.h
index 180b8a8..87a19f3 100644
--- a/src/libEGL/Surface.h
+++ b/src/libEGL/Surface.h
@@ -26,6 +26,7 @@
 {
   public:
     Surface(Display *display, const egl::Config *config, HWND window);
+    Surface(Display *display, const egl::Config *config, EGLint width, EGLint height);
 
     ~Surface();
 
@@ -41,6 +42,8 @@
     virtual IDirect3DSurface9 *getRenderTarget();
     virtual IDirect3DSurface9 *getDepthStencil();
 
+    HANDLE getShareHandle() { return mShareHandle; }
+
     void setSwapInterval(EGLint interval);
     bool checkForOutOfDateSwapChain();   // Returns true if swapchain changed due to resize or interval update
 
@@ -51,6 +54,9 @@
     IDirect3DSwapChain9 *mSwapChain;
     IDirect3DSurface9 *mDepthStencil;
     IDirect3DSurface9* mRenderTarget;
+    IDirect3DTexture9* mOffscreenTexture;
+
+    HANDLE mShareHandle;
 
     void subclassWindow();
     void unsubclassWindow();
diff --git a/src/libEGL/libEGL.cpp b/src/libEGL/libEGL.cpp
index 4f97fd5..bc40763 100644
--- a/src/libEGL/libEGL.cpp
+++ b/src/libEGL/libEGL.cpp
@@ -194,7 +194,7 @@
           case EGL_CLIENT_APIS:
             return success("OpenGL_ES");
           case EGL_EXTENSIONS:
-            return success("");
+            return display->getExtensionString();
           case EGL_VENDOR:
             return success("Google Inc.");
           case EGL_VERSION:
@@ -391,15 +391,61 @@
     try
     {
         egl::Display *display = static_cast<egl::Display*>(dpy);
+        EGLint width = 0, height = 0;
 
         if (!validate(display, config))
         {
             return EGL_NO_SURFACE;
         }
 
-        UNIMPLEMENTED();   // FIXME
+        if (attrib_list)
+        {
+            while (*attrib_list != EGL_NONE)
+            {
+                switch (attrib_list[0])
+                {
+                  case EGL_WIDTH:
+                    width = attrib_list[1];
+                    break;
+                  case EGL_HEIGHT:
+                    height = attrib_list[1];
+                    break;
+                  case EGL_LARGEST_PBUFFER:
+                    if (attrib_list[1] != EGL_FALSE)
+                      UNIMPLEMENTED(); // FIXME
+                    break;
+                  case EGL_TEXTURE_FORMAT:
+                  case EGL_TEXTURE_TARGET:
+                    switch (attrib_list[1])
+                    {
+                      case EGL_NO_TEXTURE:
+                        break;
+                      default:
+                        return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+                    }
+                    break;
+                  case EGL_MIPMAP_TEXTURE:
+                    if (attrib_list[1] != EGL_FALSE)
+                      return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+                    break;
+                  case EGL_VG_COLORSPACE:
+                    return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
+                  case EGL_VG_ALPHA_FORMAT:
+                    return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
+                  default:
+                    return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+                }
 
-        return success(EGL_NO_DISPLAY);
+                attrib_list += 2;
+            }
+        }
+
+        if (width == 0 || height == 0)
+          return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+
+        EGLSurface surface = (EGLSurface)display->createOffscreenSurface(width, height, config);
+
+        return success(surface);
     }
     catch(std::bad_alloc&)
     {
@@ -425,7 +471,7 @@
 
         UNIMPLEMENTED();   // FIXME
 
-        return success(EGL_NO_DISPLAY);
+        return success(EGL_NO_SURFACE);
     }
     catch(std::bad_alloc&)
     {
@@ -550,6 +596,46 @@
     return EGL_FALSE;
 }
 
+EGLBoolean __stdcall eglQuerySurfacePointerANGLE(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void **value)
+{
+    TRACE("(EGLDisplay dpy = 0x%0.8p, EGLSurface surface = 0x%0.8p, EGLint attribute = %d, void **value = 0x%0.8p)",
+          dpy, surface, attribute, value);
+
+    try
+    {
+        egl::Display *display = static_cast<egl::Display*>(dpy);
+
+        if (!validate(display))
+        {
+            return EGL_FALSE;
+        }
+
+        if (surface == EGL_NO_SURFACE)
+        {
+            return error(EGL_BAD_SURFACE, EGL_FALSE);
+        }
+
+        egl::Surface *eglSurface = (egl::Surface*)surface;
+
+        switch (attribute)
+        {
+          case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE:
+            *value = (void*) eglSurface->getShareHandle();
+            break;
+          default:
+            return error(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+        }
+
+        return success(EGL_TRUE);
+    }
+    catch(std::bad_alloc&)
+    {
+        return error(EGL_BAD_ALLOC, EGL_FALSE);
+    }
+
+    return EGL_FALSE;
+}
+
 EGLBoolean __stdcall eglBindAPI(EGLenum api)
 {
     EVENT("(EGLenum api = 0x%X)", api);
@@ -699,9 +785,9 @@
             return EGL_FALSE;
         }
 
-        UNIMPLEMENTED();   // FIXME
+        // FIXME - need implementation
 
-        return success(EGL_TRUE);
+        return success(EGL_FALSE);
     }
     catch(std::bad_alloc&)
     {
@@ -724,9 +810,9 @@
             return EGL_FALSE;
         }
 
-        UNIMPLEMENTED();   // FIXME
+        // FIXME - need implementation
 
-        return success(EGL_TRUE);
+        return success(EGL_FALSE);
     }
     catch(std::bad_alloc&)
     {
@@ -1093,6 +1179,7 @@
 
         static const Extension eglExtensions[] =
         {
+            {"eglQuerySurfacePointerANGLE", (__eglMustCastToProperFunctionPointerType)eglQuerySurfacePointerANGLE},
             {"", NULL},
         };
 
diff --git a/src/libEGL/main.h b/src/libEGL/main.h
index d6dc759..d09d9e6 100644
--- a/src/libEGL/main.h
+++ b/src/libEGL/main.h
@@ -11,6 +11,7 @@
 
 #define EGLAPI
 #include <EGL/egl.h>
+#include <EGL/eglext.h>
 
 namespace egl
 {