Bug 1213429 - Recycle e10s D3D9 textures and add partial upload support. r=Bas draft
authorBenoit Girard <b56girard@gmail.com>
Mon, 25 Jan 2016 14:58:46 -0800
changeset 325567 bd0fa6260410709b695cb533e5d8caaedf44923c
parent 324322 7104d650a97d895cbbc64d53462bf86a04658abe
child 513470 579aab99ed14a3e189064ed0b7b2f87e41011848
push id10001
push userb56girard@gmail.com
push dateMon, 25 Jan 2016 22:58:42 +0000
reviewersBas
bugs1213429
milestone46.0a1
Bug 1213429 - Recycle e10s D3D9 textures and add partial upload support. r=Bas
gfx/layers/d3d9/TextureD3D9.cpp
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -329,22 +329,24 @@ DataTextureSourceD3D9::Update(gfx::DataS
   // It will be ignored. Incremental update with a source offset is only used
   // on Mac so it is not clear that we ever will need to support it for D3D.
   MOZ_ASSERT(!aSrcOffset);
 
   if (!mCompositor || !mCompositor->device()) {
     NS_WARNING("No D3D device to update the texture.");
     return false;
   }
-  mSize = aSurface->GetSize();
 
-  uint32_t bpp = 0;
+  uint32_t bpp = BytesPerPixel(aSurface->GetFormat());
+  DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
+
+  mSize = aSurface->GetSize();
+  mFormat = aSurface->GetFormat();
 
   _D3DFORMAT format = D3DFMT_A8R8G8B8;
-  mFormat = aSurface->GetFormat();
   switch (mFormat) {
   case SurfaceFormat::B8G8R8X8:
     format = D3DFMT_X8R8G8B8;
     bpp = 4;
     break;
   case SurfaceFormat::B8G8R8A8:
     format = D3DFMT_A8R8G8B8;
     bpp = 4;
@@ -354,49 +356,118 @@ DataTextureSourceD3D9::Update(gfx::DataS
     bpp = 1;
     break;
   default:
     NS_WARNING("Bad image format");
     return false;
   }
 
   int32_t maxSize = mCompositor->GetMaxTextureSize();
-  DeviceManagerD3D9* deviceManager = gfxWindowsPlatform::GetPlatform()->GetD3D9DeviceManager();
   if ((mSize.width <= maxSize && mSize.height <= maxSize) ||
-      (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
-    mTexture = DataToTexture(deviceManager,
-                             aSurface->GetData(), aSurface->Stride(),
-                             IntSize(mSize), format, bpp);
+    (mFlags & TextureFlags::DISALLOW_BIGIMAGE)) {
+
+    if (mTexture) {
+      D3DSURFACE_DESC currentDesc;
+      mTexture->GetLevelDesc(0, &currentDesc);
+
+      // Make sure there's no size mismatch, if there is, recreate.
+      if (currentDesc.Width != mSize.width || currentDesc.Height != mSize.height ||
+        currentDesc.Format != format) {
+        mTexture = nullptr;
+        // Make sure we upload the whole surface.
+        aDestRegion = nullptr;
+      }
+    }
+
     if (!mTexture) {
-      NS_WARNING("Could not upload texture");
+      // TODO Improve: Reallocating this texture is costly enough
+      //      that it causes us to skip frames on scrolling
+      //      important pages like Facebook.
+      mTexture = deviceManager->CreateTexture(mSize, format, D3DPOOL_DEFAULT, this);
+      mIsTiled = false;
+      if (!mTexture) {
+        Reset();
+        return false;
+      }
+
+      if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+        aDestRegion = nullptr;
+      }
+    }
+
+    DataSourceSurface::MappedSurface map;
+    if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+      gfxCriticalError() << "Failed to map surface.";
       Reset();
       return false;
     }
-    mIsTiled = false;
-  } else {
-    mIsTiled = true;
-    uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) *
-                         GetRequiredTilesD3D9(mSize.height, maxSize);
-    mTileTextures.resize(tileCount);
-    mTexture = nullptr;
+
+    nsIntRegion regionToUpdate = aDestRegion ? *aDestRegion : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height));
+
+    RefPtr<IDirect3DSurface9> srcSurface;
+    HRESULT hr = mCompositor->device()->CreateOffscreenPlainSurface(mSize.width, mSize.height, format, D3DPOOL_SYSTEMMEM, getter_AddRefs(srcSurface), nullptr);
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    RefPtr<IDirect3DSurface9> destSurface;
+    mTexture->GetSurfaceLevel(0, getter_AddRefs(destSurface));
+
+    D3DLOCKED_RECT rect;
+    srcSurface->LockRect(&rect, nullptr, 0);
+
+    nsIntRegionRectIterator iter(regionToUpdate);
+    const IntRect *iterRect;
+    while ((iterRect = iter.Next())) {
+      uint8_t* src = map.mData + map.mStride * iterRect->y + BytesPerPixel(aSurface->GetFormat()) * iterRect->x;
+      uint8_t* dest = reinterpret_cast<uint8_t*>(rect.pBits) + rect.Pitch * iterRect->y + BytesPerPixel(aSurface->GetFormat()) * iterRect->x;
+
+      for (int y = 0; y < iterRect->height; y++) {
+        memcpy(dest + rect.Pitch * y,
+          src + map.mStride * y,
+          iterRect->width * bpp);
+      }
+    }
+
+    srcSurface->UnlockRect();
+    aSurface->Unmap();
 
-    for (uint32_t i = 0; i < tileCount; i++) {
-      IntRect tileRect = GetTileRect(i);
-      unsigned char* data = aSurface->GetData() +
-                            tileRect.y * aSurface->Stride() +
-                            tileRect.x * bpp;
-      mTileTextures[i] = DataToTexture(deviceManager,
-                                       data,
-                                       aSurface->Stride(),
-                                       IntSize(tileRect.width, tileRect.height),
-                                       format,
-                                       bpp);
-      if (!mTileTextures[i]) {
-        NS_WARNING("Could not upload texture");
-        Reset();
+    iter = nsIntRegionRectIterator(regionToUpdate);
+    while ((iterRect = iter.Next())) {
+
+      RECT updateRect;
+      updateRect.left = iterRect->x;
+      updateRect.top = iterRect->y;
+      updateRect.right = iterRect->XMost();
+      updateRect.bottom = iterRect->YMost();
+      POINT point = { updateRect.left, updateRect.top };
+
+      mCompositor->device()->UpdateSurface(srcSurface, &updateRect, destSurface, &point);
+    }
+  } else {
+    mIsTiled = true;
+    uint32_t tileCount = GetRequiredTilesD3D9(mSize.width, maxSize) *
+                         GetRequiredTilesD3D9(mSize.height, maxSize);
+    mTileTextures.resize(tileCount);
+    mTexture = nullptr;
+
+    for (uint32_t i = 0; i < tileCount; i++) {
+      IntRect tileRect = GetTileRect(i);
+      unsigned char* data = aSurface->GetData() +
+                            tileRect.y * aSurface->Stride() +
+                            tileRect.x * bpp;
+      mTileTextures[i] = DataToTexture(deviceManager,
+                                       data,
+                                       aSurface->Stride(),
+                                       IntSize(tileRect.width, tileRect.height),
+                                       format,
+                                       bpp);
+      if (!mTileTextures[i]) {
+        NS_WARNING("Could not upload texture");
+        Reset();
         return false;
       }
     }
   }
 
   return true;
 }