gfx/layers/wr/WebRenderUserData.cpp
author Nicolas Silva <nsilva@mozilla.com>
Wed, 13 Feb 2019 09:21:11 +0000
changeset 458877 96b6da57e237722e313af1da3fd97f0e4439a80a
parent 454688 d536f45f5b166c4b9562caf0c41991cb5df831cc
child 459592 43678f936e37673d325bd3bf3d300ea69aad5bae
permissions -rw-r--r--
Bug 1527441 - Separate WebRenderImageData and WebRenderFallbackData. r=jrmuizel This patch is an attempt to separate the two and avoid accidentally getting into an inconsistent state. WebRenderImageData manages resources for images that are shared with webrender, while WebRenderFallbackData manages state to render content, in most case in a blob image. if the fallback data needs to render into a TextureClient, it creates a WebRenderImageData to hold on to the shared texture, but does not inherit from it anymore. Differential Revision: https://phabricator.services.mozilla.com/D19565

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "WebRenderUserData.h"

#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/ImageClient.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/RenderRootStateManager.h"
#include "mozilla/layers/WebRenderMessages.h"
#include "mozilla/layers/IpcResourceUpdateQueue.h"
#include "mozilla/layers/SharedSurfacesChild.h"
#include "nsDisplayListInvalidation.h"
#include "nsIFrame.h"
#include "WebRenderCanvasRenderer.h"

namespace mozilla {
namespace layers {

void WebRenderBackgroundData::AddWebRenderCommands(
    wr::DisplayListBuilder& aBuilder) {
  aBuilder.PushRect(mBounds, mBounds, true, mColor);
}

/* static */ bool WebRenderUserData::SupportsAsyncUpdate(nsIFrame* aFrame) {
  if (!aFrame) {
    return false;
  }
  RefPtr<WebRenderImageData> data = GetWebRenderUserData<WebRenderImageData>(
      aFrame, static_cast<uint32_t>(DisplayItemType::TYPE_VIDEO));
  if (data) {
    return data->IsAsync();
  }

  return false;
}

/* static */ bool WebRenderUserData::ProcessInvalidateForImage(
    nsIFrame* aFrame, DisplayItemType aType) {
  MOZ_ASSERT(aFrame);

  if (!aFrame->HasProperty(WebRenderUserDataProperty::Key())) {
    return false;
  }

  auto type = static_cast<uint32_t>(aType);
  RefPtr<WebRenderFallbackData> fallback =
      GetWebRenderUserData<WebRenderFallbackData>(aFrame, type);
  if (fallback) {
    fallback->SetInvalid(true);
    aFrame->SchedulePaint();
    return true;
  }

  RefPtr<WebRenderImageData> image =
      GetWebRenderUserData<WebRenderImageData>(aFrame, type);
  if (image && image->UsingSharedSurface()) {
    return true;
  }

  aFrame->SchedulePaint();
  return false;
}

WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
                                     uint32_t aDisplayItemKey,
                                     nsIFrame* aFrame)
    : mManager(aManager),
      mFrame(aFrame),
      mDisplayItemKey(aDisplayItemKey),
      mTable(aManager->GetWebRenderUserDataTable()),
      mUsed(false) {}

WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
                                     nsDisplayItem* aItem)
    : mManager(aManager),
      mFrame(aItem->Frame()),
      mDisplayItemKey(aItem->GetPerFrameKey()),
      mTable(aManager->GetWebRenderUserDataTable()),
      mUsed(false) {}

WebRenderUserData::~WebRenderUserData() {}

void WebRenderUserData::RemoveFromTable() { mTable->RemoveEntry(this); }

WebRenderBridgeChild* WebRenderUserData::WrBridge() const {
  return mManager->WrBridge();
}

WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
                                       nsDisplayItem* aItem)
    : WebRenderUserData(aManager, aItem), mOwnsKey(false) {}

WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
                                       uint32_t aDisplayItemKey,
                                       nsIFrame* aFrame)
    : WebRenderUserData(aManager, aDisplayItemKey, aFrame), mOwnsKey(false) {}

WebRenderImageData::~WebRenderImageData() {
  ClearImageKey();

  if (mPipelineId) {
    mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
  }
}

bool WebRenderImageData::UsingSharedSurface() const {
  if (!mContainer || !mKey || mOwnsKey) {
    return false;
  }

  // If this is just an update with the same image key, then we know that the
  // share request initiated an asynchronous update so that we don't need to
  // rebuild the scene.
  wr::ImageKey key;
  nsresult rv = SharedSurfacesChild::Share(
      mContainer, mManager, mManager->AsyncResourceUpdates(), key);
  return NS_SUCCEEDED(rv) && mKey.ref() == key;
}

void WebRenderImageData::ClearImageKey() {
  if (mKey) {
    // If we don't own the key, then the owner is responsible for discarding the
    // key when appropriate.
    if (mOwnsKey) {
      mManager->AddImageKeyForDiscard(mKey.value());
      if (mTextureOfImage) {
        WrBridge()->ReleaseTextureOfImage(mKey.value());
        mTextureOfImage = nullptr;
      }
    }
    mKey.reset();
  }
  mOwnsKey = false;
  MOZ_ASSERT(!mTextureOfImage);
}

Maybe<wr::ImageKey> WebRenderImageData::UpdateImageKey(
    ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources,
    bool aFallback) {
  MOZ_ASSERT(aContainer);

  if (mContainer != aContainer) {
    mContainer = aContainer;
  }

  wr::WrImageKey key;
  if (!aFallback) {
    nsresult rv =
        SharedSurfacesChild::Share(aContainer, mManager, aResources, key);
    if (NS_SUCCEEDED(rv)) {
      // Ensure that any previously owned keys are released before replacing. We
      // don't own this key, the surface itself owns it, so that it can be
      // shared across multiple elements.
      ClearImageKey();
      mKey = Some(key);
      return mKey;
    }

    if (rv != NS_ERROR_NOT_IMPLEMENTED) {
      // We should be using the shared surface but somehow sharing it failed.
      ClearImageKey();
      return Nothing();
    }
  }

  CreateImageClientIfNeeded();
  if (!mImageClient) {
    return Nothing();
  }

  MOZ_ASSERT(mImageClient->AsImageClientSingle());

  ImageClientSingle* imageClient = mImageClient->AsImageClientSingle();
  uint32_t oldCounter = imageClient->GetLastUpdateGenerationCounter();

  bool ret = imageClient->UpdateImage(aContainer, /* unused */ 0);
  RefPtr<TextureClient> currentTexture = imageClient->GetForwardedTexture();
  if (!ret || !currentTexture) {
    // Delete old key
    ClearImageKey();
    return Nothing();
  }

  // Reuse old key if generation is not updated.
  if (!aFallback &&
      oldCounter == imageClient->GetLastUpdateGenerationCounter() && mKey) {
    return mKey;
  }

  // If we already had a texture and the format hasn't changed, better to reuse
  // the image keys than create new ones.
  bool useUpdate = mKey.isSome() && !!mTextureOfImage && !!currentTexture &&
                   mTextureOfImage->GetSize() == currentTexture->GetSize() &&
                   mTextureOfImage->GetFormat() == currentTexture->GetFormat();

  wr::MaybeExternalImageId extId = currentTexture->GetExternalImageKey();
  MOZ_RELEASE_ASSERT(extId.isSome());

  if (useUpdate) {
    MOZ_ASSERT(mKey.isSome());
    MOZ_ASSERT(mTextureOfImage);
    aResources.PushExternalImageForTexture(
        extId.ref(), mKey.ref(), currentTexture, /* aIsUpdate */ true);
  } else {
    ClearImageKey();
    key = WrBridge()->GetNextImageKey();
    aResources.PushExternalImageForTexture(extId.ref(), key, currentTexture,
                                           /* aIsUpdate */ false);
    mKey = Some(key);
  }

  mTextureOfImage = currentTexture;
  mOwnsKey = true;

  return mKey;
}

already_AddRefed<ImageClient> WebRenderImageData::GetImageClient() {
  RefPtr<ImageClient> imageClient = mImageClient;
  return imageClient.forget();
}

void WebRenderImageData::CreateAsyncImageWebRenderCommands(
    mozilla::wr::DisplayListBuilder& aBuilder, ImageContainer* aContainer,
    const StackingContextHelper& aSc, const LayoutDeviceRect& aBounds,
    const LayoutDeviceRect& aSCBounds, const gfx::Matrix4x4& aSCTransform,
    const gfx::MaybeIntSize& aScaleToSize, const wr::ImageRendering& aFilter,
    const wr::MixBlendMode& aMixBlendMode, bool aIsBackfaceVisible) {
  MOZ_ASSERT(aContainer->IsAsync());

  if (mPipelineId.isSome() && mContainer != aContainer) {
    // In this case, we need to remove the existed pipeline and create new one
    // because the ImageContainer is changed.
    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
    mPipelineId.reset();
  }

  if (!mPipelineId) {
    // Alloc async image pipeline id.
    mPipelineId =
        Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
    WrBridge()->AddPipelineIdForAsyncCompositable(
        mPipelineId.ref(), aContainer->GetAsyncContainerHandle());
    mContainer = aContainer;
  }
  MOZ_ASSERT(!mImageClient);

  // Push IFrame for async image pipeline.
  //
  // We don't push a stacking context for this async image pipeline here.
  // Instead, we do it inside the iframe that hosts the image. As a result,
  // a bunch of the calculations normally done as part of that stacking
  // context need to be done manually and pushed over to the parent side,
  // where it will be done when we build the display list for the iframe.
  // That happens in AsyncImagePipelineManager.
  wr::LayoutRect r = wr::ToRoundedLayoutRect(aBounds);
  aBuilder.PushIFrame(r, aIsBackfaceVisible, mPipelineId.ref(),
                      /*ignoreMissingPipelines*/ false);

  WrBridge()->AddWebRenderParentCommand(
      OpUpdateAsyncImagePipeline(mPipelineId.value(), aSCBounds, aSCTransform,
                                 aScaleToSize, aFilter, aMixBlendMode));
}

void WebRenderImageData::CreateImageClientIfNeeded() {
  if (!mImageClient) {
    mImageClient = ImageClient::CreateImageClient(
        CompositableType::IMAGE, WrBridge(), TextureFlags::DEFAULT);
    if (!mImageClient) {
      return;
    }

    mImageClient->Connect();
  }
}

WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager,
                                             nsDisplayItem* aItem)
    : WebRenderUserData(aManager, aItem), mInvalid(false) {}

WebRenderFallbackData::~WebRenderFallbackData() { ClearImageKey(); }

void WebRenderFallbackData::SetBlobImageKey(const wr::BlobImageKey& aKey) {
  ClearImageKey();
  mBlobKey = Some(aKey);
}

Maybe<wr::ImageKey> WebRenderFallbackData::GetImageKey() {
  if (mBlobKey) {
    return Some(wr::AsImageKey(mBlobKey.value()));
  }

  if (mImageData) {
    return mImageData->GetImageKey();
  }

  return Nothing();
}

void WebRenderFallbackData::ClearImageKey() {
  if (mImageData) {
    mImageData->ClearImageKey();
    mImageData = nullptr;
  }

  if (mBlobKey) {
    mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
    mBlobKey.reset();
  }
}

WebRenderImageData* WebRenderFallbackData::PaintIntoImage() {
  if (mBlobKey) {
    mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
    mBlobKey.reset();
  }

  if (mImageData) {
    return mImageData.get();
  }

  mImageData = MakeAndAddRef<WebRenderImageData>(mManager.get(), mDisplayItemKey, mFrame);

  return mImageData.get();
}


WebRenderAnimationData::WebRenderAnimationData(RenderRootStateManager* aManager,
                                               nsDisplayItem* aItem)
    : WebRenderUserData(aManager, aItem) {}

WebRenderAnimationData::~WebRenderAnimationData() {
  // It may be the case that nsDisplayItem that created this WebRenderUserData
  // gets destroyed without getting a chance to discard the compositor animation
  // id, so we should do it as part of cleanup here.
  uint64_t animationId = mAnimationInfo.GetCompositorAnimationsId();
  // animationId might be 0 if mAnimationInfo never held any active animations.
  if (animationId) {
    mManager->AddCompositorAnimationsIdForDiscard(animationId);
  }
}

WebRenderCanvasData::WebRenderCanvasData(RenderRootStateManager* aManager,
                                         nsDisplayItem* aItem)
    : WebRenderUserData(aManager, aItem) {}

WebRenderCanvasData::~WebRenderCanvasData() {
  if (mCanvasRenderer) {
    mCanvasRenderer->ClearCachedResources();
  }
}

void WebRenderCanvasData::ClearCanvasRenderer() { mCanvasRenderer = nullptr; }

WebRenderCanvasRendererAsync* WebRenderCanvasData::GetCanvasRenderer() {
  return mCanvasRenderer.get();
}

WebRenderCanvasRendererAsync* WebRenderCanvasData::CreateCanvasRenderer() {
  mCanvasRenderer = MakeUnique<WebRenderCanvasRendererAsync>(mManager);
  return mCanvasRenderer.get();
}

void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable) {
  for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
    iter.UserData()->RemoveFromTable();
  }
  delete aTable;
}

}  // namespace layers
}  // namespace mozilla