Exemplo n.º 1
0
already_AddRefed<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
{
  if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget() || aDT->IsCaptureDT()) {
    gfxDevCrash(LogReason::FilterNodeD2D1Target) << "Incompatible draw target type! " << (int)aDT->IsTiledDrawTarget() << " " << (int)aDT->IsDualDrawTarget();
    return nullptr;
  }
  switch (aDT->GetBackendType()) {
    case BackendType::DIRECT2D1_1:
      return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);
    default:
      gfxDevCrash(LogReason::FilterNodeD2D1Backend) << "Unknown draw target type! " << (int)aDT->GetBackendType();
      return nullptr;
  }
}
PTextureParent*
CrossProcessCompositorBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                                        const LayersBackend& aLayersBackend,
                                                        const TextureFlags& aFlags,
                                                        const uint64_t& aId,
                                                        const uint64_t& aSerial)
{
  CompositorBridgeParent::LayerTreeState* state = nullptr;

  LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
  if (sIndirectLayerTrees.end() != itr) {
    state = &itr->second;
  }

  TextureFlags flags = aFlags;

  if (!state || state->mPendingCompositorUpdates) {
    // The compositor was recreated, and we're receiving layers updates for a
    // a layer manager that will soon be discarded or invalidated. We can't
    // return null because this will mess up deserialization later and we'll
    // kill the content process. Instead, we signal that the underlying
    // TextureHost should not attempt to access the compositor.
    flags |= TextureFlags::INVALID_COMPOSITOR;
  } else if (state->mLayerManager && state->mLayerManager->GetCompositor() &&
             aLayersBackend != state->mLayerManager->GetCompositor()->GetBackendType()) {
    gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch) << "Texture backend is wrong";
  }

  return TextureHost::CreateIPDLActor(this, aSharedData, aLayersBackend, aFlags, aSerial);
}
Exemplo n.º 3
0
bool
DataSourceSurfaceD2D1::Map(MapType aMapType, MappedSurface *aMappedSurface)
{
  // DataSourceSurfaces used with the new Map API should not be used with GetData!!
  MOZ_ASSERT(!mMapped);
  MOZ_ASSERT(!mIsMapped);

  D2D1_MAP_OPTIONS options;
  if (aMapType == MapType::READ) {
    options = D2D1_MAP_OPTIONS_READ;
  } else {
    gfxDevCrash(LogReason::D2D1NoWriteMap) << "No support for Write maps on D2D1 DataSourceSurfaces yet!";
    return false;
  }

  D2D1_MAPPED_RECT map;
  if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &map))) {
    gfxCriticalError() << "Failed to map bitmap (M).";
    return false;
  }
  aMappedSurface->mData = map.bits;
  aMappedSurface->mStride = map.pitch;

  mIsMapped = !!aMappedSurface->mData;
  return mIsMapped;
}
void
AsyncTransactionWaiter::WaitComplete()
{
  MOZ_ASSERT(!InImageBridgeChildThread());

  MonitorAutoLock mon(mCompletedMonitor);
  int count = 0;
  const int maxCount = 5;
  while (mWaitCount > 0 && (count < maxCount)) {
    if (!NS_SUCCEEDED(mCompletedMonitor.Wait(PR_MillisecondsToInterval(10000)))) {
      NS_WARNING("Failed to wait Monitor");
      return;
    }
    if (count > 1) {
      printf_stderr("Waiting async transaction complete.\n");
    }
    count++;
  }

  if (mWaitCount > 0) {
    printf_stderr("Timeout of waiting transaction complete.");
  }

  if (count == maxCount) {
    gfxDevCrash(gfx::LogReason::AsyncTransactionTimeout) << "Bug 1244883: AsyncTransactionWaiter timed out.";
  }
}
Exemplo n.º 5
0
RefPtr<UnscaledFont>
GetUnscaledFont(Translator *aTranslator, wr::FontKey key) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  auto i = sFontDataTable.find(key);
  if (i == sFontDataTable.end()) {
    gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to get UnscaledFont entry for FontKey " << key.mHandle
                                                 << " because " << sFontDeleteLog.Find(key);
    return nullptr;
  }
  FontTemplate &data = i->second;
  if (data.mUnscaledFont) {
    return data.mUnscaledFont;
  }
  MOZ_ASSERT(data.mData);
  FontType type =
#ifdef XP_MACOSX
    FontType::MAC;
#elif defined(XP_WIN)
    FontType::DWRITE;
#elif defined(ANDROID)
    FontType::FREETYPE;
#else
    FontType::FONTCONFIG;
#endif
  // makes a copy of the data
  RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource((uint8_t*)data.mData, data.mSize,
                                                                              aTranslator->GetReferenceDrawTarget()->GetBackendType(),
                                                                              type,
                                                                              aTranslator->GetFontContext());
  RefPtr<UnscaledFont> unscaledFont;
  if (!fontResource) {
    gfxDevCrash(LogReason::NativeFontResourceNotFound) << "Failed to create NativeFontResource for FontKey " << key.mHandle;
  } else {
    // Instance data is only needed for GDI fonts which webrender does not
    // support.
    unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
    if (!unscaledFont) {
      gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to create UnscaledFont for FontKey " << key.mHandle;
    }
  }
  data.mUnscaledFont = unscaledFont;
  return unscaledFont;
}
Exemplo n.º 6
0
bool
ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
{
  AutoDC dc;
  AutoSelectFont font(dc.GetDC(), &mLogFont);

  // Check for a font collection first.
  uint32_t table = 0x66637474; // 'ttcf'
  uint32_t tableSize = ::GetFontData(dc.GetDC(), table, 0, nullptr, 0);
  if (tableSize == GDI_ERROR) {
    // Try as if just a single font.
    table = 0;
    tableSize = ::GetFontData(dc.GetDC(), table, 0, nullptr, 0);
    if (tableSize == GDI_ERROR) {
      return false;
    }
  }

  UniquePtr<uint8_t[]> fontData(new uint8_t[tableSize]);

  uint32_t sizeGot =
    ::GetFontData(dc.GetDC(), table, 0, fontData.get(), tableSize);
  if (sizeGot != tableSize) {
    return false;
  }

  // If it's a font collection then attempt to get the index.
  uint32_t index = 0;
  if (table != 0) {
    UniquePtr<SFNTData> sfntData = SFNTData::Create(fontData.get(),
                                                    tableSize);
    if (!sfntData) {
      gfxWarning() << "Failed to create SFNTData for GetFontFileData.";
      return false;
    }

    // We cast here because for VS2015 char16_t != wchar_t, even though they are
    // both 16 bit.
    if (!sfntData->GetIndexForU16Name(
          reinterpret_cast<char16_t*>(mLogFont.lfFaceName), &index, LF_FACESIZE - 1)) {
      gfxWarning() << "Failed to get index for face name.";
      gfxDevCrash(LogReason::GetFontFileDataFailed) <<
        "Failed to get index for face name |" <<
        NS_ConvertUTF16toUTF8(mLogFont.lfFaceName).get() << "|.";
      return false;
    }
  }

  aDataCallback(fontData.get(), tableSize, index, mSize, aBaton);
  return true;
}
Exemplo n.º 7
0
void
ContentClientBasic::CreateBuffer(ContentType aType,
                                 const IntRect& aRect,
                                 uint32_t aFlags,
                                 RefPtr<gfx::DrawTarget>* aBlackDT,
                                 RefPtr<gfx::DrawTarget>* aWhiteDT)
{
  MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
  if (aFlags & BUFFER_COMPONENT_ALPHA) {
    gfxDevCrash(LogReason::AlphaWithBasicClient) << "Asking basic content client for component alpha";
  }

  *aBlackDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
    IntSize(aRect.width, aRect.height),
    gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
}
Exemplo n.º 8
0
static bool LockD3DTexture(T* aTexture)
{
  MOZ_ASSERT(aTexture);
  RefPtr<IDXGIKeyedMutex> mutex;
  aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
  // Textures created by the DXVA decoders don't have a mutex for synchronization
  if (mutex) {
    HRESULT hr = mutex->AcquireSync(0, 10000);
    if (hr == WAIT_TIMEOUT) {
      gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
    }

    if (FAILED(hr)) {
      NS_WARNING("Failed to lock the texture");
      return false;
    }
  }
  return true;
}
void ClientSingleTiledLayerBuffer::PaintThebes(
    const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion,
    const nsIntRegion& aDirtyRegion,
    LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData,
    TilePaintFlags aFlags) {
  mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive);
  bool asyncPaint = !!(aFlags & TilePaintFlags::Async);

  // Compare layer valid region size to current backbuffer size, discard if not
  // matching.
  gfx::IntSize size = aNewValidRegion.GetBounds().Size();
  gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
  nsIntRegion paintRegion = aPaintRegion;

  RefPtr<TextureClient> discardedFrontBuffer = nullptr;
  RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
  nsIntRegion discardedValidRegion;

  if (mSize != size || mTilingOrigin != origin) {
    discardedFrontBuffer = mTile.mFrontBuffer;
    discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
    discardedValidRegion = mValidRegion;

    TILING_LOG(
        "TILING %p: Single-tile valid region changed. Discarding buffers.\n",
        &mPaintedLayer);
    ResetPaintedAndValidState();
    mSize = size;
    mTilingOrigin = origin;
    paintRegion = aNewValidRegion;
  }

  SurfaceMode mode;
  gfxContentType content = GetContentType(&mode);
  mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);

  if (mTile.IsPlaceholderTile()) {
    mTile.SetTextureAllocator(this);
  }

  if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) {
    AutoTArray<uint64_t, 2> syncTextureSerials;
    mTile.GetSyncTextureSerials(mode, syncTextureSerials);
    if (syncTextureSerials.Length() > 0) {
      mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials);
    }
  }

  // The dirty region relative to the top-left of the tile.
  nsIntRegion tileVisibleRegion = aNewValidRegion.MovedBy(-mTilingOrigin);
  nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);

  Maybe<AcquiredBackBuffer> backBuffer =
      mTile.AcquireBackBuffer(mCompositableClient, tileDirtyRegion,
                              tileVisibleRegion, content, mode, aFlags);

  if (!backBuffer) {
    return;
  }

  // Mark the area we need to paint in the back buffer as invalid in the
  // front buffer as they will become out of sync.
  mTile.mInvalidFront.OrWith(tileDirtyRegion);

  // Add backbuffer's invalid region to the dirty region to be painted.
  // This will be empty if we were able to copy from the front in to the back.
  nsIntRegion tileInvalidRegion = mTile.mInvalidBack;
  tileInvalidRegion.AndWith(tileVisibleRegion);

  paintRegion.OrWith(tileInvalidRegion.MovedBy(mTilingOrigin));
  tileDirtyRegion.OrWith(tileInvalidRegion);

  // Mark the region we will be painting and the region we copied from the front
  // buffer as needing to be uploaded to the compositor
  mTile.mUpdateRect =
      tileDirtyRegion.GetBounds().Union(backBuffer->mUpdatedRect);

  // If the old frontbuffer was discarded then attempt to copy what we
  // can from it to the new backbuffer.
  if (discardedFrontBuffer) {
    nsIntRegion copyableRegion;
    copyableRegion.And(aNewValidRegion, discardedValidRegion);
    copyableRegion.SubOut(aDirtyRegion);

    OpenMode readMode =
        asyncPaint ? OpenMode::OPEN_READ_ASYNC : OpenMode::OPEN_READ;

    DualTextureClientAutoLock discardedBuffer(
        discardedFrontBuffer, discardedFrontBufferOnWhite, readMode);

    if (discardedBuffer.Succeeded()) {
      RefPtr<gfx::SourceSurface> discardedSurface = discardedBuffer->Snapshot();

      for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
        const gfx::IntRect src =
            iter.Get() - discardedValidRegion.GetBounds().TopLeft();
        const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;

        backBuffer->mTarget->CopySurface(discardedSurface, src, dest);
      }

      TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n",
                 &mPaintedLayer, Stringify(copyableRegion).c_str());

      // We don't need to repaint valid content that was just copied.
      paintRegion.SubOut(copyableRegion);
      copyableRegion.MoveBy(-mTilingOrigin);
      tileDirtyRegion.SubOut(copyableRegion);
    } else {
      gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front "
                      "buffer's draw target";
    }
  }

  if (mode != SurfaceMode::SURFACE_OPAQUE) {
    for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
      const gfx::Rect drawRect(iter.Get().X(), iter.Get().Y(),
                               iter.Get().Width(), iter.Get().Height());
      backBuffer->mTarget->ClearRect(drawRect);
    }
  }

  // Paint into the target
  {
    RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(backBuffer->mTarget);
    if (!ctx) {
      gfxDevCrash(gfx::LogReason::InvalidContext)
          << "SingleTiledContextClient context problem "
          << gfx::hexa(backBuffer->mTarget);
      return;
    }
    ctx->SetMatrix(
        ctx->CurrentMatrix().PreTranslate(-mTilingOrigin.x, -mTilingOrigin.y));

    aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion,
              DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
  }

  if (asyncPaint) {
    if (!backBuffer->mCapture->IsEmpty()) {
      UniquePtr<PaintTask> task(new PaintTask());
      task->mCapture = backBuffer->mCapture;
      task->mTarget = backBuffer->mBackBuffer;
      task->mClients = std::move(backBuffer->mTextureClients);
      if (discardedFrontBuffer) {
        task->mClients.AppendElement(discardedFrontBuffer);
      }
      if (discardedFrontBufferOnWhite) {
        task->mClients.AppendElement(discardedFrontBufferOnWhite);
      }

      // The target is an alias for the capture, and the paint thread expects
      // to be the only one with a reference to the capture
      backBuffer->mTarget = nullptr;
      backBuffer->mCapture = nullptr;

      PaintThread::Get()->QueuePaintTask(std::move(task));
      mManager->SetQueuedAsyncPaints();
    }
  } else {
    MOZ_ASSERT(backBuffer->mTarget == backBuffer->mBackBuffer);
    MOZ_ASSERT(!backBuffer->mCapture);
  }

  // The new buffer is now validated, remove the dirty region from it.
  mTile.mInvalidBack.SubOut(tileDirtyRegion);

  backBuffer = Nothing();

  mTile.Flip();
  UnlockTile(mTile);

  mValidRegion = aNewValidRegion;
  mLastPaintSurfaceMode = mode;
  mLastPaintContentType = content;
}
void
ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                          const nsIntRegion& aPaintRegion,
                                          const nsIntRegion& aDirtyRegion,
                                          LayerManager::DrawPaintedLayerCallback aCallback,
                                          void* aCallbackData,
                                          bool aIsProgressive)
{
  mWasLastPaintProgressive = aIsProgressive;

  // Compare layer valid region size to current backbuffer size, discard if not matching.
  gfx::IntSize size = aNewValidRegion.GetBounds().Size();
  gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
  nsIntRegion paintRegion = aPaintRegion;

  RefPtr<TextureClient> discardedFrontBuffer = nullptr;
  RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
  nsIntRegion discardedValidRegion;

  if (mSize != size ||
      mTilingOrigin != origin) {
    discardedFrontBuffer = mTile.mFrontBuffer;
    discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
    discardedValidRegion = mValidRegion;

    TILING_LOG("TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer)
;
    ResetPaintedAndValidState();
    mSize = size;
    mTilingOrigin = origin;
    paintRegion = aNewValidRegion;
  }

  SurfaceMode mode;
  gfxContentType content = GetContentType(&mode);
  mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);

  if (mTile.IsPlaceholderTile()) {
    mTile.SetTextureAllocator(this);
  }

  // The dirty region relative to the top-left of the tile.
  nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);

  nsIntRegion extraPainted;
  RefPtr<TextureClient> backBufferOnWhite;
  RefPtr<TextureClient> backBuffer =
    mTile.GetBackBuffer(mCompositableClient,
                        tileDirtyRegion,
                        content, mode,
                        extraPainted,
                        &backBufferOnWhite);

  mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds());

  extraPainted.MoveBy(mTilingOrigin);
  extraPainted.And(extraPainted, aNewValidRegion);
  mPaintedRegion.OrWith(paintRegion);
  mPaintedRegion.OrWith(extraPainted);

  if (!backBuffer) {
    return;
  }

  RefPtr<gfx::DrawTarget> dt = backBuffer->BorrowDrawTarget();
  RefPtr<gfx::DrawTarget> dtOnWhite;
  if (backBufferOnWhite) {
    dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
  }

  if (mode != SurfaceMode::SURFACE_OPAQUE) {
    for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
      const gfx::IntRect& rect = iter.Get();
      if (dtOnWhite) {
        dt->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
                     gfx::ColorPattern(gfx::Color(0.0, 0.0, 0.0, 1.0)));
        dtOnWhite->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
                            gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)));
      } else {
        dt->ClearRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
      }
    }
  }

  // If the old frontbuffer was discarded then attempt to copy what we
  // can from it to the new backbuffer.
  if (discardedFrontBuffer) {
    nsIntRegion copyableRegion;
    copyableRegion.And(aNewValidRegion, discardedValidRegion);
    copyableRegion.SubOut(aDirtyRegion);

    if (!copyableRegion.IsEmpty()) {
      TextureClientAutoLock frontLock(discardedFrontBuffer,
                                      OpenMode::OPEN_READ);
      if (frontLock.Succeeded()) {
        for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
          const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
          const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
          discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest);
        }
      }

      if (discardedFrontBufferOnWhite && backBufferOnWhite) {
        TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
                                               OpenMode::OPEN_READ);
        if (frontOnWhiteLock.Succeeded()) {
          for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
            const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
            const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;

            discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
                                                             &rect, &dest);
          }
        }
      }

      TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());

      // We don't need to repaint valid content that was just copied.
      paintRegion.SubOut(copyableRegion);
    }
  }

  if (dtOnWhite) {
    dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
    dtOnWhite = nullptr;
  }

  {
    RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
    if (!ctx) {
      gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
      return;
    }
    ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));

    aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
  }

  // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
  // now out of sync.
  mTile.mInvalidFront.OrWith(tileDirtyRegion);

  // The new buffer is now validated, remove the dirty region from it.
  mTile.mInvalidBack.SubOut(tileDirtyRegion);

  dt = nullptr;

  mTile.Flip();
  UnlockTile(mTile);

  if (backBuffer->HasIntermediateBuffer()) {
    // If our new buffer has an internal buffer, we don't want to keep another
    // TextureClient around unnecessarily, so discard the back-buffer.
    mTile.DiscardBackBuffer();
  }

  mValidRegion = aNewValidRegion;
  mLastPaintSurfaceMode = mode;
  mLastPaintContentType = content;
}
Exemplo n.º 11
0
void
BasicCompositor::DrawQuad(const gfx::Rect& aRect,
                          const gfx::Rect& aClipRect,
                          const EffectChain &aEffectChain,
                          gfx::Float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Rect& aVisibleRect)
{
  RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget;

  // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,
  // |dest| is a temporary surface.
  RefPtr<DrawTarget> dest = buffer;

  AutoRestoreTransform autoRestoreTransform(dest);

  Matrix newTransform;
  Rect transformBounds;
  Matrix4x4 new3DTransform;
  IntPoint offset = mRenderTarget->GetOrigin();

  if (aTransform.Is2D()) {
    newTransform = aTransform.As2D();
  } else {
    // Create a temporary surface for the transform.
    dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
    if (!dest) {
      return;
    }

    dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y));

    // Get the bounds post-transform.
    transformBounds = aTransform.TransformAndClipBounds(aRect, Rect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height));
    transformBounds.RoundOut();

    if (transformBounds.IsEmpty()) {
      return;
    }

    // Propagate the coordinate offset to our 2D draw target.
    newTransform = Matrix::Translation(transformBounds.x, transformBounds.y);

    // When we apply the 3D transformation, we do it against a temporary
    // surface, so undo the coordinate offset.
    new3DTransform = Matrix4x4::Translation(aRect.x, aRect.y, 0) * aTransform;
  }

  buffer->PushClipRect(aClipRect);

  newTransform.PostTranslate(-offset.x, -offset.y);
  buffer->SetTransform(newTransform);

  RefPtr<SourceSurface> sourceMask;
  Matrix maskTransform;
  if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
    EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
    sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest);
    if (!sourceMask) {
      gfxWarning() << "Invalid sourceMask effect";
    }
    MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
    MOZ_ASSERT(!effectMask->mIs3D);
    maskTransform = effectMask->mMaskTransform.As2D();
    maskTransform.PreTranslate(-offset.x, -offset.y);
  }

  CompositionOp blendMode = CompositionOp::OP_OVER;
  if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
    blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
  }

  switch (aEffectChain.mPrimaryEffect->mType) {
    case EffectTypes::SOLID_COLOR: {
      EffectSolidColor* effectSolidColor =
        static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());

      bool unboundedOp = !IsOperatorBoundByMask(blendMode);
      if (unboundedOp) {
        dest->PushClipRect(aRect);
      }

      FillRectWithMask(dest, aRect, effectSolidColor->mColor,
                       DrawOptions(aOpacity, blendMode), sourceMask, &maskTransform);

      if (unboundedOp) {
        dest->PopClip();
      }
      break;
    }
    case EffectTypes::RGB: {
      TexturedEffect* texturedEffect =
          static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
      TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic();

      if (source && texturedEffect->mPremultiplied) {
          DrawSurfaceWithTextureCoords(dest, aRect,
                                       source->GetSurface(dest),
                                       texturedEffect->mTextureCoords,
                                       texturedEffect->mFilter,
                                       DrawOptions(aOpacity, blendMode),
                                       sourceMask, &maskTransform);
      } else if (source) {
        SourceSurface* srcSurf = source->GetSurface(dest);
        if (srcSurf) {
          RefPtr<DataSourceSurface> srcData = srcSurf->GetDataSurface();

          // Yes, we re-create the premultiplied data every time.
          // This might be better with a cache, eventually.
          RefPtr<DataSourceSurface> premultData = gfxUtils::CreatePremultipliedDataSurface(srcData);

          DrawSurfaceWithTextureCoords(dest, aRect,
                                       premultData,
                                       texturedEffect->mTextureCoords,
                                       texturedEffect->mFilter,
                                       DrawOptions(aOpacity, blendMode),
                                       sourceMask, &maskTransform);
        }
      } else {
        gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect) << "Bad for basic with " << texturedEffect->mTexture->Name() << " and " << gfx::hexa(sourceMask);
      }

      break;
    }
    case EffectTypes::YCBCR: {
      NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
      break;
    }
    case EffectTypes::RENDER_TARGET: {
      EffectRenderTarget* effectRenderTarget =
        static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
      RefPtr<BasicCompositingRenderTarget> surface
        = static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get());
      RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot();

      DrawSurfaceWithTextureCoords(dest, aRect,
                                   sourceSurf,
                                   effectRenderTarget->mTextureCoords,
                                   effectRenderTarget->mFilter,
                                   DrawOptions(aOpacity, blendMode),
                                   sourceMask, &maskTransform);
      break;
    }
    case EffectTypes::COMPONENT_ALPHA: {
      NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!");
      break;
    }
    default: {
      NS_RUNTIMEABORT("Invalid effect type!");
      break;
    }
  }

  if (!aTransform.Is2D()) {
    dest->Flush();

    RefPtr<SourceSurface> snapshot = dest->Snapshot();
    RefPtr<DataSourceSurface> source = snapshot->GetDataSurface();
    RefPtr<DataSourceSurface> temp =
      Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8
#ifdef MOZ_ENABLE_SKIA
        , true
#endif
        );
    if (NS_WARN_IF(!temp)) {
      buffer->PopClip();
      return;
    }

    Transform(temp, source, new3DTransform, transformBounds.TopLeft());

    transformBounds.MoveTo(0, 0);
    buffer->DrawSurface(temp, transformBounds, transformBounds);
  }

  buffer->PopClip();
}