Bug 734404 - Part 3: Add code to test plugin to support DXGI async drawing. r=roc
authorBas Schouten <bschouten@mozilla.com>
Wed, 30 May 2012 07:14:30 +0200
changeset 95238 93908fff3b0ca2d008b20e50c51bc44480c36a77
parent 95237 671dee55dab054fd15951c4aaab0f51546cdac9b
child 95239 96b85fac12431bfa9afb1b9a8055800cc1f2fe15
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersroc
bugs734404
milestone15.0a1
Bug 734404 - Part 3: Add code to test plugin to support DXGI async drawing. r=roc
dom/plugins/test/testplugin/README
dom/plugins/test/testplugin/nptest.cpp
dom/plugins/test/testplugin/nptest.h
dom/plugins/test/testplugin/nptest_platform.h
dom/plugins/test/testplugin/nptest_windows.cpp
--- a/dom/plugins/test/testplugin/README
+++ b/dom/plugins/test/testplugin/README
@@ -9,22 +9,33 @@ The test plugin registers itself for the
 * getEventModel()
 Returns the NPAPI event model in use. On platforms without event models,
 simply returns 0;
 
 == Rendering ==
 
 By default, the plugin fills its rectangle with gray, with a black border, and
 renders the user-agent string (obtained from NPN_UserAgent) centered in black.
+This rendering method is not supported for the async drawing models.
 
 The test plugin supports the following parameters:
 
 * drawmode="solid"
 The plugin will draw a solid color instead of the default rendering described
 above. The default solid color is completely transparent black (i.e., nothing).
+This should be specified when using one of the async models.
+
+* asyncmodel="bitmap"
+The plugin will use the NPAPI Async Bitmap drawing model extension. On
+unsupported platforms this will fallback to non-async rendering.
+
+* asyncmodel="dxgi"
+The plugin will use the NPAPI Async DXGI drawing model extension. Only
+supported on Windows Vista or higher. On unsupported platforms this will
+fallback to non-async rendering.
 
 * color
 This specifies the color to use for drawmode="solid". The value should be 8 hex
 digits, 2 per channel in AARRGGBB format.
 
 == Generic API Tests ==
 
 * setUndefinedValueTest
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -819,16 +819,21 @@ NPP_New(NPMIMEType pluginType, NPP insta
       }
     }
     else if (strcmp(argn[i], "asyncmodel") == 0) {
       if (strcmp(argv[i], "bitmap") == 0) {
         if (pluginSupportsAsyncBitmapDrawing()) {
           instanceData->asyncDrawing = AD_BITMAP;
         }
       }
+      if (strcmp(argv[i], "dxgi") == 0) {
+        if (pluginSupportsAsyncDXGIDrawing()) {
+          instanceData->asyncDrawing = AD_DXGI;
+        }
+      }
     }
     if (strcmp(argn[i], "streammode") == 0) {
       if (strcmp(argv[i], "normal") == 0) {
         instanceData->streamMode = NP_NORMAL;
       }
       else if ((strcmp(argv[i], "asfile") == 0) &&
                 strlen(argv[i]) == strlen("asfile")) {
         instanceData->streamMode = NP_ASFILE;
@@ -931,16 +936,27 @@ NPP_New(NPMIMEType pluginType, NPP insta
     NPBool supportsAsyncBitmap = false;
     if ((NPN_GetValue(instance, NPNVsupportsAsyncBitmapSurfaceBool, &supportsAsyncBitmap) == NPERR_NO_ERROR) &&
         supportsAsyncBitmap) {
       NPN_SetValue(instance, NPPVpluginDrawingModel, (void*)NPDrawingModelAsyncBitmapSurface);
     } else {
       instanceData->asyncDrawing = AD_NONE;
     }
   }
+#ifdef XP_WIN
+  else if (instanceData->asyncDrawing == AD_DXGI) {
+    NPBool supportsAsyncDXGI = false;
+    if ((NPN_GetValue(instance, NPNVsupportsAsyncWindowsDXGISurfaceBool, &supportsAsyncDXGI) == NPERR_NO_ERROR) &&
+        supportsAsyncDXGI) {
+      NPN_SetValue(instance, NPPVpluginDrawingModel, (void*)NPDrawingModelAsyncWindowsDXGISurface);
+    } else {
+      instanceData->asyncDrawing = AD_NONE;
+    }
+  }
+#endif
 
   instanceData->lastReportedPrivateModeState = false;
   instanceData->lastMouseX = instanceData->lastMouseY = -1;
   instanceData->widthAtLastPaint = -1;
   instanceData->paintCount = 0;
 
   // do platform-specific initialization
   NPError err = pluginInstanceInit(instanceData);
@@ -2732,16 +2748,21 @@ setColor(NPObject* npobj, const NPVarian
   r.top = 0;
   r.right = id->window.width;
   r.bottom = id->window.height;
   if (id->asyncDrawing == AD_NONE) {
     NPN_InvalidateRect(npp, &r);
   } else if (id->asyncDrawing == AD_BITMAP) {
     drawAsyncBitmapColor(id);
   }
+#ifdef XP_WIN
+  else if (id->asyncDrawing == AD_DXGI) {
+    pluginDrawAsyncDxgiColor(id);
+  }
+#endif
 
   VOID_TO_NPVARIANT(*result);
   return true;
 }
 
 void notifyDidPaint(InstanceData* instanceData)
 {
   ++instanceData->paintCount;
--- a/dom/plugins/test/testplugin/nptest.h
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -58,17 +58,18 @@ typedef enum {
   FUNCTION_NPP_WRITEREADY,
   FUNCTION_NPP_WRITE,
   FUNCTION_NPP_DESTROYSTREAM,
   FUNCTION_NPP_WRITE_RPC
 } TestFunction;
 
 typedef enum {
   AD_NONE,
-  AD_BITMAP
+  AD_BITMAP,
+  AD_DXGI
 } AsyncDrawing;
 
 typedef enum {
   ACTIVATION_STATE_UNKNOWN,
   ACTIVATION_STATE_ACTIVATED,
   ACTIVATION_STATE_DEACTIVATED
 } ActivationState;
 
--- a/dom/plugins/test/testplugin/nptest_platform.h
+++ b/dom/plugins/test/testplugin/nptest_platform.h
@@ -49,16 +49,28 @@ bool    pluginSupportsWindowMode();
 bool    pluginSupportsWindowlessMode();
 
 /**
  * Returns true if the plugin supports async bitmap drawing.
  */
 bool    pluginSupportsAsyncBitmapDrawing();
 
 /**
+ * Returns true if the plugin supports DXGI bitmap drawing.
+ */
+static bool    pluginSupportsAsyncDXGIDrawing()
+{
+#ifdef XP_WIN
+  return true;
+#else
+  return false;
+#endif
+}
+
+/**
  * Initialize the plugin instance. Returning an error here will cause the
  * plugin instantiation to fail.
  */
 NPError pluginInstanceInit(InstanceData* instanceData);
 
 /**
  * Shutdown the plugin instance.
  */
@@ -76,16 +88,20 @@ void    pluginDoSetWindow(InstanceData* 
 void    pluginWidgetInit(InstanceData* instanceData, void* oldWindow);
 
 /**
  * Handle an event for a windowless plugin. (Windowed plugins are
  * responsible for listening for their own events.)
  */
 int16_t pluginHandleEvent(InstanceData* instanceData, void* event);
 
+#ifdef XP_WIN
+void    pluginDrawAsyncDxgiColor(InstanceData* instanceData);
+#endif
+
 enum RectEdge {
   EDGE_LEFT = 0,
   EDGE_TOP = 1,
   EDGE_RIGHT = 2,
   EDGE_BOTTOM = 3
 };
 
 enum {
--- a/dom/plugins/test/testplugin/nptest_windows.cpp
+++ b/dom/plugins/test/testplugin/nptest_windows.cpp
@@ -33,24 +33,44 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nptest_platform.h"
 
 #include <windows.h>
 #include <windowsx.h>
 #include <stdio.h>
 
+#include <d3d10_1.h>
+
+typedef HRESULT (WINAPI*D3D10CreateDevice1Func)(
+  IDXGIAdapter *pAdapter,
+  D3D10_DRIVER_TYPE DriverType,
+  HMODULE Software,
+  UINT Flags,
+  D3D10_FEATURE_LEVEL1 HardwareLevel,
+  UINT SDKVersion,
+  ID3D10Device1 **ppDevice
+);
+
+typedef HRESULT(WINAPI*CreateDXGIFactory1Func)(
+  REFIID riid,
+  void **ppFactory
+);
+
  using namespace std;
 
 void SetSubclass(HWND hWnd, InstanceData* instanceData);
 void ClearSubclass(HWND hWnd);
 LRESULT CALLBACK PluginWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
 struct _PlatformData {
   HWND childWindow;
+  ID3D10Device1 *device;
+  ID3D10Texture2D *frontBuffer;
+  ID3D10Texture2D *backBuffer;
 };
 
 bool
 pluginSupportsWindowMode()
 {
   return true;
 }
 
@@ -72,31 +92,149 @@ pluginInstanceInit(InstanceData* instanc
   NPP npp = instanceData->npp;
 
   instanceData->platformData = static_cast<PlatformData*>
     (NPN_MemAlloc(sizeof(PlatformData)));
   if (!instanceData->platformData)
     return NPERR_OUT_OF_MEMORY_ERROR;
   
   instanceData->platformData->childWindow = NULL;
+  instanceData->platformData->device = NULL;
+  instanceData->platformData->frontBuffer = NULL;
+  instanceData->platformData->backBuffer = NULL;
   return NPERR_NO_ERROR;
 }
 
 void
 pluginInstanceShutdown(InstanceData* instanceData)
 {
+  PlatformData *pd = instanceData->platformData;
+  if (pd->frontBuffer) {
+    pd->frontBuffer->Release();
+  }
+  if (pd->backBuffer) {
+    pd->backBuffer->Release();
+  }
+  if (pd->device) {
+    pd->device->Release();
+  }
   NPN_MemFree(instanceData->platformData);
   instanceData->platformData = 0;
 }
 
+static ID3D10Device1*
+getD3D10Device()
+{
+  ID3D10Device1 *device;
+    
+  HMODULE d3d10module = LoadLibraryA("d3d10_1.dll");
+  D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func)
+      GetProcAddress(d3d10module, "D3D10CreateDevice1");
+
+  if (createD3DDevice) {
+    HMODULE dxgiModule = LoadLibraryA("dxgi.dll");
+    CreateDXGIFactory1Func createDXGIFactory1 = (CreateDXGIFactory1Func)
+        GetProcAddress(dxgiModule, "CreateDXGIFactory1");
+
+    HRESULT hr;
+
+    // Try to use a DXGI 1.1 adapter in order to share resources
+    // across processes.
+    IDXGIAdapter1 *adapter1;
+    if (createDXGIFactory1) {
+      IDXGIFactory1 *factory1;
+      hr = createDXGIFactory1(__uuidof(IDXGIFactory1),
+                              (void**)&factory1);
+
+      if (FAILED(hr) || !factory1) {
+        // Uh-oh
+        return NULL;
+      }
+
+      hr = factory1->EnumAdapters1(0, &adapter1);
+
+      if (SUCCEEDED(hr) && adapter1) {
+        hr = adapter1->CheckInterfaceSupport(__uuidof(ID3D10Device),
+                                             NULL);
+        if (FAILED(hr)) {
+            adapter1 = NULL;
+        }
+      }
+      factory1->Release();
+    }
+
+    hr = createD3DDevice(
+          adapter1, 
+          D3D10_DRIVER_TYPE_HARDWARE,
+          NULL,
+          D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+          D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+          D3D10_FEATURE_LEVEL_10_0,
+          D3D10_1_SDK_VERSION,
+          &device);
+
+    adapter1->Release();
+  }
+
+  return device;
+}
+
 void
 pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow)
 {
   instanceData->window = *newWindow;
   NPP npp = instanceData->npp;
+
+  if (instanceData->asyncDrawing == AD_DXGI) {
+    if (instanceData->frontBuffer &&
+        instanceData->frontBuffer->size.width == newWindow->width &&
+        instanceData->frontBuffer->size.height == newWindow->height) {
+          return;
+    }
+    if (instanceData->frontBuffer) {
+      instanceData->platformData->frontBuffer->Release();
+      instanceData->platformData->frontBuffer = NULL;
+      NPN_FinalizeAsyncSurface(npp, instanceData->frontBuffer);
+      NPN_MemFree(instanceData->frontBuffer);
+    }
+    if (instanceData->backBuffer) {
+      instanceData->platformData->backBuffer->Release();
+      instanceData->platformData->backBuffer = NULL;
+      NPN_FinalizeAsyncSurface(npp, instanceData->backBuffer);
+      NPN_MemFree(instanceData->backBuffer);
+    }
+
+    if (!instanceData->platformData->device) {
+      instanceData->platformData->device = getD3D10Device();
+    }
+
+    ID3D10Device1 *dev = instanceData->platformData->device;
+
+    if (!dev) {
+      return;
+    }
+
+    instanceData->frontBuffer = (NPAsyncSurface*)NPN_MemAlloc(sizeof(NPAsyncSurface));
+    instanceData->backBuffer = (NPAsyncSurface*)NPN_MemAlloc(sizeof(NPAsyncSurface));
+
+    NPSize size;
+    size.width = newWindow->width;
+    size.height = newWindow->height;
+
+    memset(instanceData->frontBuffer, 0, sizeof(NPAsyncSurface));
+    memset(instanceData->backBuffer, 0, sizeof(NPAsyncSurface));
+
+    NPN_InitAsyncSurface(npp, &size, NPImageFormatBGRA32, NULL, instanceData->frontBuffer);
+    NPN_InitAsyncSurface(npp, &size, NPImageFormatBGRA32, NULL, instanceData->backBuffer);
+
+    dev->OpenSharedResource(instanceData->frontBuffer->sharedHandle, __uuidof(ID3D10Texture2D), (void**)&instanceData->platformData->frontBuffer);
+    dev->OpenSharedResource(instanceData->backBuffer->sharedHandle, __uuidof(ID3D10Texture2D), (void**)&instanceData->platformData->backBuffer);
+
+    pluginDrawAsyncDxgiColor(instanceData);
+  }
 }
 
 #define CHILD_WIDGET_SIZE 10
 
 void
 pluginWidgetInit(InstanceData* instanceData, void* oldWindow)
 {
   HWND hWnd = (HWND)instanceData->window.window;
@@ -570,8 +708,50 @@ void pluginDoInternalConsistencyCheck(In
     HWND hWnd = (HWND)instanceData->window.window;
     ::GetWindowRect(hWnd, &ourRect);
     checkEquals(childRect.left, ourRect.left, "Child widget left", error);
     checkEquals(childRect.top, ourRect.top, "Child widget top", error);
     checkEquals(childRect.right, childRect.left + CHILD_WIDGET_SIZE, "Child widget width", error);
     checkEquals(childRect.bottom, childRect.top + CHILD_WIDGET_SIZE, "Child widget height", error);
   }
 }
+
+void
+pluginDrawAsyncDxgiColor(InstanceData* id)
+{
+  PlatformData *pd = id->platformData;
+
+  ID3D10Device1 *dev = pd->device;
+
+  IDXGIKeyedMutex *mutex;
+  pd->backBuffer->QueryInterface(&mutex);
+
+  mutex->AcquireSync(0, INFINITE);
+  ID3D10RenderTargetView *rtView;
+  dev->CreateRenderTargetView(pd->backBuffer, NULL, &rtView);
+
+  PRUint32 rgba = id->scriptableObject->drawColor;
+
+  unsigned char subpixels[4];
+  subpixels[0] = rgba & 0xFF;
+  subpixels[1] = (rgba & 0xFF00) >> 8;
+  subpixels[2] = (rgba & 0xFF0000) >> 16;
+  subpixels[3] = (rgba & 0xFF000000) >> 24;
+
+  float color[4];
+  color[2] = float(subpixels[3] * subpixels[0]) / 0xFE01;
+  color[1] = float(subpixels[3] * subpixels[1]) / 0xFE01;
+  color[0] = float(subpixels[3] * subpixels[2]) / 0xFE01;
+  color[3] = float(subpixels[3]) / 0xFF;
+  dev->ClearRenderTargetView(rtView, color);
+  rtView->Release();
+
+  mutex->ReleaseSync(0);
+  mutex->Release();
+
+  NPN_SetCurrentAsyncSurface(id->npp, id->backBuffer, NULL);
+  NPAsyncSurface *oldFront = id->frontBuffer;
+  id->frontBuffer = id->backBuffer;
+  id->backBuffer = oldFront;
+  ID3D10Texture2D *oldFrontT = pd->frontBuffer;
+  pd->frontBuffer = pd->backBuffer;
+  pd->backBuffer = oldFrontT;
+}