forked from mirrors/gecko-dev
		
	This patch was generated automatically by the "modeline.py" script, available here: https://github.com/amccreight/moz-source-tools/blob/master/modeline.py For every file that is modified in this patch, the changes are as follows: (1) The patch changes the file to use the exact C++ mode lines from the Mozilla coding style guide, available here: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style#Mode_Line (2) The patch deletes any blank lines between the mode line & the MPL boilerplate comment. (3) If the file previously had the mode lines and MPL boilerplate in a single contiguous C++ comment, then the patch splits them into separate C++ comments, to match the boilerplate in the coding style. MozReview-Commit-ID: 77D61xpSmIl --HG-- extra : rebase_source : c6162fa3cf539a07177a19838324bf368faa162b
		
			
				
	
	
		
			876 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			876 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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 "ImageContainer.h"
 | 
						|
#include <string.h>                     // for memcpy, memset
 | 
						|
#include "GLImages.h"                   // for SurfaceTextureImage
 | 
						|
#include "gfx2DGlue.h"
 | 
						|
#include "gfxPlatform.h"                // for gfxPlatform
 | 
						|
#include "gfxUtils.h"                   // for gfxUtils
 | 
						|
#include "libyuv.h"
 | 
						|
#include "mozilla/RefPtr.h"             // for already_AddRefed
 | 
						|
#include "mozilla/ipc/CrossProcessMutex.h"  // for CrossProcessMutex, etc
 | 
						|
#include "mozilla/layers/CompositorTypes.h"
 | 
						|
#include "mozilla/layers/ImageBridgeChild.h"  // for ImageBridgeChild
 | 
						|
#include "mozilla/layers/ImageClient.h"  // for ImageClient
 | 
						|
#include "mozilla/layers/LayersMessages.h"
 | 
						|
#include "mozilla/layers/SharedPlanarYCbCrImage.h"
 | 
						|
#include "mozilla/layers/SharedRGBImage.h"
 | 
						|
#include "mozilla/layers/TextureClientRecycleAllocator.h"
 | 
						|
#include "mozilla/gfx/gfxVars.h"
 | 
						|
#include "nsISupportsUtils.h"           // for NS_IF_ADDREF
 | 
						|
#include "YCbCrUtils.h"                 // for YCbCr conversions
 | 
						|
#include "gfx2DGlue.h"
 | 
						|
#include "mozilla/gfx/2D.h"
 | 
						|
#include "mozilla/CheckedInt.h"
 | 
						|
 | 
						|
#ifdef XP_MACOSX
 | 
						|
#include "mozilla/gfx/QuartzSupport.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef XP_WIN
 | 
						|
#include "gfxWindowsPlatform.h"
 | 
						|
#include <d3d10_1.h>
 | 
						|
#include "mozilla/gfx/DeviceManagerDx.h"
 | 
						|
#include "mozilla/layers/D3D11YCbCrImage.h"
 | 
						|
#endif
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace layers {
 | 
						|
 | 
						|
using namespace mozilla::ipc;
 | 
						|
using namespace android;
 | 
						|
using namespace mozilla::gfx;
 | 
						|
 | 
						|
Atomic<int32_t> Image::sSerialCounter(0);
 | 
						|
 | 
						|
Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
 | 
						|
 | 
						|
RefPtr<PlanarYCbCrImage>
 | 
						|
ImageFactory::CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin *aRecycleBin)
 | 
						|
{
 | 
						|
  return new RecyclingPlanarYCbCrImage(aRecycleBin);
 | 
						|
}
 | 
						|
 | 
						|
BufferRecycleBin::BufferRecycleBin()
 | 
						|
  : mLock("mozilla.layers.BufferRecycleBin.mLock")
 | 
						|
  // This member is only valid when the bin is not empty and will be properly
 | 
						|
  // initialized in RecycleBuffer, but initializing it here avoids static analysis
 | 
						|
  // noise.
 | 
						|
  , mRecycledBufferSize(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
BufferRecycleBin::RecycleBuffer(UniquePtr<uint8_t[]> aBuffer, uint32_t aSize)
 | 
						|
{
 | 
						|
  MutexAutoLock lock(mLock);
 | 
						|
 | 
						|
  if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
 | 
						|
    mRecycledBuffers.Clear();
 | 
						|
  }
 | 
						|
  mRecycledBufferSize = aSize;
 | 
						|
  mRecycledBuffers.AppendElement(Move(aBuffer));
 | 
						|
}
 | 
						|
 | 
						|
UniquePtr<uint8_t[]>
 | 
						|
BufferRecycleBin::GetBuffer(uint32_t aSize)
 | 
						|
{
 | 
						|
  MutexAutoLock lock(mLock);
 | 
						|
 | 
						|
  if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) {
 | 
						|
    return UniquePtr<uint8_t[]>(new (fallible) uint8_t[aSize]);
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t last = mRecycledBuffers.Length() - 1;
 | 
						|
  UniquePtr<uint8_t[]> result = Move(mRecycledBuffers[last]);
 | 
						|
  mRecycledBuffers.RemoveElementAt(last);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
BufferRecycleBin::ClearRecycledBuffers()
 | 
						|
{
 | 
						|
  MutexAutoLock lock(mLock);
 | 
						|
  if (!mRecycledBuffers.IsEmpty()) {
 | 
						|
    mRecycledBuffers.Clear();
 | 
						|
  }
 | 
						|
  mRecycledBufferSize = 0;
 | 
						|
}
 | 
						|
 | 
						|
ImageContainerListener::ImageContainerListener(ImageContainer* aImageContainer)
 | 
						|
  : mLock("mozilla.layers.ImageContainerListener.mLock")
 | 
						|
  , mImageContainer(aImageContainer)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
ImageContainerListener::~ImageContainerListener()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainerListener::NotifyComposite(const ImageCompositeNotification& aNotification)
 | 
						|
{
 | 
						|
  MutexAutoLock lock(mLock);
 | 
						|
  if (mImageContainer) {
 | 
						|
    mImageContainer->NotifyComposite(aNotification);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainerListener::ClearImageContainer()
 | 
						|
{
 | 
						|
  MutexAutoLock lock(mLock);
 | 
						|
  mImageContainer = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainerListener::DropImageClient()
 | 
						|
{
 | 
						|
  MutexAutoLock lock(mLock);
 | 
						|
  if (mImageContainer) {
 | 
						|
    mImageContainer->DropImageClient();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<ImageClient>
 | 
						|
ImageContainer::GetImageClient()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock mon(mRecursiveMutex);
 | 
						|
  EnsureImageClient();
 | 
						|
  RefPtr<ImageClient> imageClient = mImageClient;
 | 
						|
  return imageClient.forget();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::DropImageClient()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock mon(mRecursiveMutex);
 | 
						|
  if (mImageClient) {
 | 
						|
    mImageClient->ClearCachedResources();
 | 
						|
    mImageClient = nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::EnsureImageClient()
 | 
						|
{
 | 
						|
  // If we're not forcing a new ImageClient, then we can skip this if we don't have an existing
 | 
						|
  // ImageClient, or if the existing one belongs to an IPC actor that is still open.
 | 
						|
  if (!mIsAsync) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (mImageClient && mImageClient->GetForwarder()->GetLayersIPCActor()->IPCOpen()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
 | 
						|
  if (imageBridge) {
 | 
						|
    mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this);
 | 
						|
    if (mImageClient) {
 | 
						|
      mAsyncContainerHandle = mImageClient->GetAsyncHandle();
 | 
						|
    } else {
 | 
						|
      // It's okay to drop the async container handle since the ImageBridgeChild
 | 
						|
      // is going to die anyway.
 | 
						|
      mAsyncContainerHandle = CompositableHandle();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
ImageContainer::ImageContainer(Mode flag)
 | 
						|
: mRecursiveMutex("ImageContainer.mRecursiveMutex"),
 | 
						|
  mGenerationCounter(++sGenerationCounter),
 | 
						|
  mPaintCount(0),
 | 
						|
  mDroppedImageCount(0),
 | 
						|
  mImageFactory(new ImageFactory()),
 | 
						|
  mRecycleBin(new BufferRecycleBin()),
 | 
						|
  mIsAsync(flag == ASYNCHRONOUS),
 | 
						|
  mCurrentProducerID(-1)
 | 
						|
{
 | 
						|
  if (flag == ASYNCHRONOUS) {
 | 
						|
    mNotifyCompositeListener = new ImageContainerListener(this);
 | 
						|
    EnsureImageClient();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
ImageContainer::ImageContainer(const CompositableHandle& aHandle)
 | 
						|
  : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
 | 
						|
  mGenerationCounter(++sGenerationCounter),
 | 
						|
  mPaintCount(0),
 | 
						|
  mDroppedImageCount(0),
 | 
						|
  mImageFactory(nullptr),
 | 
						|
  mRecycleBin(nullptr),
 | 
						|
  mIsAsync(true),
 | 
						|
  mAsyncContainerHandle(aHandle),
 | 
						|
  mCurrentProducerID(-1)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(mAsyncContainerHandle);
 | 
						|
}
 | 
						|
 | 
						|
ImageContainer::~ImageContainer()
 | 
						|
{
 | 
						|
  if (mNotifyCompositeListener) {
 | 
						|
    mNotifyCompositeListener->ClearImageContainer();
 | 
						|
  }
 | 
						|
  if (mAsyncContainerHandle) {
 | 
						|
    if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
 | 
						|
      imageBridge->ForgetImageContainer(mAsyncContainerHandle);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
RefPtr<PlanarYCbCrImage>
 | 
						|
ImageContainer::CreatePlanarYCbCrImage()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
  EnsureImageClient();
 | 
						|
  if (mImageClient && mImageClient->AsImageClientSingle()) {
 | 
						|
    return new SharedPlanarYCbCrImage(mImageClient);
 | 
						|
  }
 | 
						|
  return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
 | 
						|
}
 | 
						|
 | 
						|
RefPtr<SharedRGBImage>
 | 
						|
ImageContainer::CreateSharedRGBImage()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
  EnsureImageClient();
 | 
						|
  if (!mImageClient || !mImageClient->AsImageClientSingle()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  return new SharedRGBImage(mImageClient);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
 | 
						|
  mGenerationCounter = ++sGenerationCounter;
 | 
						|
 | 
						|
  if (!aImages.IsEmpty()) {
 | 
						|
    NS_ASSERTION(mCurrentImages.IsEmpty() ||
 | 
						|
                 mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
 | 
						|
                 mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
 | 
						|
                 "frame IDs shouldn't go backwards");
 | 
						|
    if (aImages[0].mProducerID != mCurrentProducerID) {
 | 
						|
      mFrameIDsNotYetComposited.Clear();
 | 
						|
      mCurrentProducerID = aImages[0].mProducerID;
 | 
						|
    } else if (!aImages[0].mTimeStamp.IsNull()) {
 | 
						|
      // Check for expired frames
 | 
						|
      for (auto& img : mCurrentImages) {
 | 
						|
        if (img.mProducerID != aImages[0].mProducerID ||
 | 
						|
            img.mTimeStamp.IsNull() ||
 | 
						|
            img.mTimeStamp >= aImages[0].mTimeStamp) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
        if (!img.mComposited && !img.mTimeStamp.IsNull() &&
 | 
						|
            img.mFrameID != aImages[0].mFrameID) {
 | 
						|
          mFrameIDsNotYetComposited.AppendElement(img.mFrameID);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      // Remove really old frames, assuming they'll never be composited.
 | 
						|
      const uint32_t maxFrames = 100;
 | 
						|
      if (mFrameIDsNotYetComposited.Length() > maxFrames) {
 | 
						|
        uint32_t dropFrames = mFrameIDsNotYetComposited.Length() - maxFrames;
 | 
						|
        mDroppedImageCount += dropFrames;
 | 
						|
        mFrameIDsNotYetComposited.RemoveElementsAt(0, dropFrames);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  nsTArray<OwningImage> newImages;
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < aImages.Length(); ++i) {
 | 
						|
    NS_ASSERTION(aImages[i].mImage, "image can't be null");
 | 
						|
    NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
 | 
						|
                 "Multiple images require timestamps");
 | 
						|
    if (i > 0) {
 | 
						|
      NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
 | 
						|
                   "Timestamps must not decrease");
 | 
						|
      NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
 | 
						|
                   "FrameIDs must increase");
 | 
						|
      NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
 | 
						|
                   "ProducerIDs must be the same");
 | 
						|
    }
 | 
						|
    OwningImage* img = newImages.AppendElement();
 | 
						|
    img->mImage = aImages[i].mImage;
 | 
						|
    img->mTimeStamp = aImages[i].mTimeStamp;
 | 
						|
    img->mFrameID = aImages[i].mFrameID;
 | 
						|
    img->mProducerID = aImages[i].mProducerID;
 | 
						|
    for (auto& oldImg : mCurrentImages) {
 | 
						|
      if (oldImg.mFrameID == img->mFrameID &&
 | 
						|
          oldImg.mProducerID == img->mProducerID) {
 | 
						|
        img->mComposited = oldImg.mComposited;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  mCurrentImages.SwapElements(newImages);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::ClearImagesFromImageBridge()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
  SetCurrentImageInternal(nsTArray<NonOwningImage>());
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!aImages.IsEmpty());
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
  if (mIsAsync) {
 | 
						|
    if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
 | 
						|
      imageBridge->UpdateImageClient(this);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  SetCurrentImageInternal(aImages);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::ClearAllImages()
 | 
						|
{
 | 
						|
  if (mImageClient) {
 | 
						|
    // Let ImageClient release all TextureClients. This doesn't return
 | 
						|
    // until ImageBridge has called ClearCurrentImageFromImageBridge.
 | 
						|
    if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
 | 
						|
      imageBridge->FlushAllImages(mImageClient, this);
 | 
						|
    }
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
  SetCurrentImageInternal(nsTArray<NonOwningImage>());
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::ClearCachedResources()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
  if (mImageClient && mImageClient->AsImageClientSingle()) {
 | 
						|
    if (!mImageClient->HasTextureClientRecycler()) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  return mRecycleBin->ClearRecycledBuffers();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::SetCurrentImageInTransaction(Image *aImage)
 | 
						|
{
 | 
						|
  AutoTArray<NonOwningImage,1> images;
 | 
						|
  images.AppendElement(NonOwningImage(aImage));
 | 
						|
  SetCurrentImagesInTransaction(images);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::SetCurrentImagesInTransaction(const nsTArray<NonOwningImage>& aImages)
 | 
						|
{
 | 
						|
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 | 
						|
  NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
 | 
						|
 | 
						|
  SetCurrentImageInternal(aImages);
 | 
						|
}
 | 
						|
 | 
						|
bool ImageContainer::IsAsync() const
 | 
						|
{
 | 
						|
  return mIsAsync;
 | 
						|
}
 | 
						|
 | 
						|
CompositableHandle ImageContainer::GetAsyncContainerHandle()
 | 
						|
{
 | 
						|
  NS_ASSERTION(IsAsync(), "Shared image ID is only relevant to async ImageContainers");
 | 
						|
  NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
 | 
						|
  RecursiveMutexAutoLock mon(mRecursiveMutex);
 | 
						|
  EnsureImageClient();
 | 
						|
  return mAsyncContainerHandle;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
ImageContainer::HasCurrentImage()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
 | 
						|
  return !mCurrentImages.IsEmpty();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
 | 
						|
                                 uint32_t* aGenerationCounter)
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
 | 
						|
  *aImages = mCurrentImages;
 | 
						|
  if (aGenerationCounter) {
 | 
						|
    *aGenerationCounter = mGenerationCounter;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
gfx::IntSize
 | 
						|
ImageContainer::GetCurrentSize()
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
 | 
						|
  if (mCurrentImages.IsEmpty()) {
 | 
						|
    return gfx::IntSize(0, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  return mCurrentImages[0].mImage->GetSize();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ImageContainer::NotifyComposite(const ImageCompositeNotification& aNotification)
 | 
						|
{
 | 
						|
  RecursiveMutexAutoLock lock(mRecursiveMutex);
 | 
						|
 | 
						|
  // An image composition notification is sent the first time a particular
 | 
						|
  // image is composited by an ImageHost. Thus, every time we receive such
 | 
						|
  // a notification, a new image has been painted.
 | 
						|
  ++mPaintCount;
 | 
						|
 | 
						|
  if (aNotification.producerID() == mCurrentProducerID) {
 | 
						|
    uint32_t i;
 | 
						|
    for (i = 0; i < mFrameIDsNotYetComposited.Length(); ++i) {
 | 
						|
      if (mFrameIDsNotYetComposited[i] <= aNotification.frameID()) {
 | 
						|
        if (mFrameIDsNotYetComposited[i] < aNotification.frameID()) {
 | 
						|
          ++mDroppedImageCount;
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    mFrameIDsNotYetComposited.RemoveElementsAt(0, i);
 | 
						|
    for (auto& img : mCurrentImages) {
 | 
						|
      if (img.mFrameID == aNotification.frameID()) {
 | 
						|
        img.mComposited = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aNotification.imageTimeStamp().IsNull()) {
 | 
						|
    mPaintDelay = aNotification.firstCompositeTimeStamp() -
 | 
						|
        aNotification.imageTimeStamp();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
#ifdef XP_WIN
 | 
						|
D3D11YCbCrRecycleAllocator*
 | 
						|
ImageContainer::GetD3D11YCbCrRecycleAllocator(KnowsCompositor* aAllocator)
 | 
						|
{
 | 
						|
  if (mD3D11YCbCrRecycleAllocator &&
 | 
						|
      aAllocator == mD3D11YCbCrRecycleAllocator->GetAllocator()) {
 | 
						|
    return mD3D11YCbCrRecycleAllocator;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetContentDevice();
 | 
						|
  if (!device) {
 | 
						|
    device = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
 | 
						|
  }
 | 
						|
 | 
						|
  LayersBackend backend = aAllocator->GetCompositorBackendType();
 | 
						|
  if (!device || backend != LayersBackend::LAYERS_D3D11) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<ID3D10Multithread> multi;
 | 
						|
  HRESULT hr =
 | 
						|
    device->QueryInterface((ID3D10Multithread**)getter_AddRefs(multi));
 | 
						|
  if (FAILED(hr) || !multi) {
 | 
						|
    gfxWarning() << "Multithread safety interface not supported. " << hr;
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  multi->SetMultithreadProtected(TRUE);
 | 
						|
 | 
						|
  mD3D11YCbCrRecycleAllocator =
 | 
						|
    new D3D11YCbCrRecycleAllocator(aAllocator, device);
 | 
						|
  return mD3D11YCbCrRecycleAllocator;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
PlanarYCbCrImage::PlanarYCbCrImage()
 | 
						|
  : Image(nullptr, ImageFormat::PLANAR_YCBCR)
 | 
						|
  , mOffscreenFormat(SurfaceFormat::UNKNOWN)
 | 
						|
  , mBufferSize(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
RecyclingPlanarYCbCrImage::~RecyclingPlanarYCbCrImage()
 | 
						|
{
 | 
						|
  if (mBuffer) {
 | 
						|
    mRecycleBin->RecycleBuffer(Move(mBuffer), mBufferSize);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
size_t
 | 
						|
RecyclingPlanarYCbCrImage::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 | 
						|
{
 | 
						|
  // Ignoring:
 | 
						|
  // - mData - just wraps mBuffer
 | 
						|
  // - Surfaces should be reported under gfx-surfaces-*:
 | 
						|
  //   - mSourceSurface
 | 
						|
  // - Base class:
 | 
						|
  //   - mImplData is not used
 | 
						|
  // Not owned:
 | 
						|
  // - mRecycleBin
 | 
						|
  size_t size = aMallocSizeOf(mBuffer.get());
 | 
						|
 | 
						|
  // Could add in the future:
 | 
						|
  // - mBackendData (from base class)
 | 
						|
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
UniquePtr<uint8_t[]>
 | 
						|
RecyclingPlanarYCbCrImage::AllocateBuffer(uint32_t aSize)
 | 
						|
{
 | 
						|
  return mRecycleBin->GetBuffer(aSize);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
CopyPlane(uint8_t *aDst, const uint8_t *aSrc,
 | 
						|
          const gfx::IntSize &aSize, int32_t aStride, int32_t aSkip)
 | 
						|
{
 | 
						|
  if (!aSkip) {
 | 
						|
    // Fast path: planar input.
 | 
						|
    memcpy(aDst, aSrc, aSize.height * aStride);
 | 
						|
  } else {
 | 
						|
    int32_t height = aSize.height;
 | 
						|
    int32_t width = aSize.width;
 | 
						|
    for (int y = 0; y < height; ++y) {
 | 
						|
      const uint8_t *src = aSrc;
 | 
						|
      uint8_t *dst = aDst;
 | 
						|
      // Slow path
 | 
						|
      for (int x = 0; x < width; ++x) {
 | 
						|
        *dst++ = *src++;
 | 
						|
        src += aSkip;
 | 
						|
      }
 | 
						|
      aSrc += aStride;
 | 
						|
      aDst += aStride;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
RecyclingPlanarYCbCrImage::CopyData(const Data& aData)
 | 
						|
{
 | 
						|
  mData = aData;
 | 
						|
 | 
						|
  // update buffer size
 | 
						|
  // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
 | 
						|
  const auto checkedSize =
 | 
						|
    CheckedInt<uint32_t>(mData.mCbCrStride) * mData.mCbCrSize.height * 2 +
 | 
						|
    CheckedInt<uint32_t>(mData.mYStride) * mData.mYSize.height;
 | 
						|
 | 
						|
  if (!checkedSize.isValid())
 | 
						|
    return false;
 | 
						|
 | 
						|
  const auto size = checkedSize.value();
 | 
						|
 | 
						|
  // get new buffer
 | 
						|
  mBuffer = AllocateBuffer(size);
 | 
						|
  if (!mBuffer)
 | 
						|
    return false;
 | 
						|
 | 
						|
  // update buffer size
 | 
						|
  mBufferSize = size;
 | 
						|
 | 
						|
  mData.mYChannel = mBuffer.get();
 | 
						|
  mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
 | 
						|
  mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
 | 
						|
 | 
						|
  CopyPlane(mData.mYChannel, aData.mYChannel,
 | 
						|
            mData.mYSize, mData.mYStride, mData.mYSkip);
 | 
						|
  CopyPlane(mData.mCbChannel, aData.mCbChannel,
 | 
						|
            mData.mCbCrSize, mData.mCbCrStride, mData.mCbSkip);
 | 
						|
  CopyPlane(mData.mCrChannel, aData.mCrChannel,
 | 
						|
            mData.mCbCrSize, mData.mCbCrStride, mData.mCrSkip);
 | 
						|
 | 
						|
  mSize = aData.mPicSize;
 | 
						|
  mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
gfxImageFormat
 | 
						|
PlanarYCbCrImage::GetOffscreenFormat()
 | 
						|
{
 | 
						|
  return mOffscreenFormat == SurfaceFormat::UNKNOWN ?
 | 
						|
    gfxVars::OffscreenFormat() :
 | 
						|
    mOffscreenFormat;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PlanarYCbCrImage::AdoptData(const Data& aData)
 | 
						|
{
 | 
						|
  mData = aData;
 | 
						|
  mSize = aData.mPicSize;
 | 
						|
  mOrigin = gfx::IntPoint(aData.mPicX, aData.mPicY);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<gfx::SourceSurface>
 | 
						|
PlanarYCbCrImage::GetAsSourceSurface()
 | 
						|
{
 | 
						|
  if (mSourceSurface) {
 | 
						|
    RefPtr<gfx::SourceSurface> surface(mSourceSurface);
 | 
						|
    return surface.forget();
 | 
						|
  }
 | 
						|
 | 
						|
  gfx::IntSize size(mSize);
 | 
						|
  gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat());
 | 
						|
  gfx::GetYCbCrToRGBDestFormatAndSize(mData, format, size);
 | 
						|
  if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
 | 
						|
      mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
 | 
						|
    NS_ERROR("Illegal image dest width or height");
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
 | 
						|
  if (NS_WARN_IF(!surface)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
 | 
						|
  if (NS_WARN_IF(!mapping.IsMapped())) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(), mapping.GetStride());
 | 
						|
 | 
						|
  mSourceSurface = surface;
 | 
						|
 | 
						|
  return surface.forget();
 | 
						|
}
 | 
						|
 | 
						|
NVImage::NVImage()
 | 
						|
  : Image(nullptr, ImageFormat::NV_IMAGE)
 | 
						|
  , mBufferSize(0)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
NVImage::~NVImage() = default;
 | 
						|
 | 
						|
IntSize
 | 
						|
NVImage::GetSize()
 | 
						|
{
 | 
						|
  return mSize;
 | 
						|
}
 | 
						|
 | 
						|
IntRect
 | 
						|
NVImage::GetPictureRect()
 | 
						|
{
 | 
						|
  return mData.GetPictureRect();
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<SourceSurface>
 | 
						|
NVImage::GetAsSourceSurface()
 | 
						|
{
 | 
						|
  if (mSourceSurface) {
 | 
						|
    RefPtr<gfx::SourceSurface> surface(mSourceSurface);
 | 
						|
    return surface.forget();
 | 
						|
  }
 | 
						|
 | 
						|
  // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
 | 
						|
  // logics in PlanarYCbCrImage::GetAsSourceSurface().
 | 
						|
  const int bufferLength = mData.mYSize.height * mData.mYStride +
 | 
						|
                           mData.mCbCrSize.height * mData.mCbCrSize.width * 2;
 | 
						|
  auto *buffer = new uint8_t[bufferLength];
 | 
						|
 | 
						|
  Data aData = mData;
 | 
						|
  aData.mCbCrStride = aData.mCbCrSize.width;
 | 
						|
  aData.mCbSkip = 0;
 | 
						|
  aData.mCrSkip = 0;
 | 
						|
  aData.mYChannel = buffer;
 | 
						|
  aData.mCbChannel = aData.mYChannel + aData.mYSize.height * aData.mYStride;
 | 
						|
  aData.mCrChannel = aData.mCbChannel + aData.mCbCrSize.height * aData.mCbCrStride;
 | 
						|
 | 
						|
  if (mData.mCbChannel < mData.mCrChannel) {  // NV12
 | 
						|
    libyuv::NV12ToI420(mData.mYChannel, mData.mYStride,
 | 
						|
                       mData.mCbChannel, mData.mCbCrStride,
 | 
						|
                       aData.mYChannel, aData.mYStride,
 | 
						|
                       aData.mCbChannel, aData.mCbCrStride,
 | 
						|
                       aData.mCrChannel, aData.mCbCrStride,
 | 
						|
                       aData.mYSize.width, aData.mYSize.height);
 | 
						|
  } else {  // NV21
 | 
						|
    libyuv::NV21ToI420(mData.mYChannel, mData.mYStride,
 | 
						|
                       mData.mCrChannel, mData.mCbCrStride,
 | 
						|
                       aData.mYChannel, aData.mYStride,
 | 
						|
                       aData.mCbChannel, aData.mCbCrStride,
 | 
						|
                       aData.mCrChannel, aData.mCbCrStride,
 | 
						|
                       aData.mYSize.width, aData.mYSize.height);
 | 
						|
  }
 | 
						|
 | 
						|
  // The logics in PlanarYCbCrImage::GetAsSourceSurface().
 | 
						|
  gfx::IntSize size(mSize);
 | 
						|
  gfx::SurfaceFormat format =
 | 
						|
    gfx::ImageFormatToSurfaceFormat(gfxPlatform::GetPlatform()->GetOffscreenFormat());
 | 
						|
  gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
 | 
						|
  if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
 | 
						|
      mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
 | 
						|
    NS_ERROR("Illegal image dest width or height");
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
 | 
						|
  if (NS_WARN_IF(!surface)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
 | 
						|
  if (NS_WARN_IF(!mapping.IsMapped())) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  gfx::ConvertYCbCrToRGB(aData, format, size, mapping.GetData(), mapping.GetStride());
 | 
						|
 | 
						|
  mSourceSurface = surface;
 | 
						|
 | 
						|
  // Release the temporary buffer.
 | 
						|
  delete[] buffer;
 | 
						|
 | 
						|
  return surface.forget();
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
NVImage::IsValid()
 | 
						|
{
 | 
						|
  return !!mBufferSize;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t
 | 
						|
NVImage::GetBufferSize() const
 | 
						|
{
 | 
						|
  return mBufferSize;
 | 
						|
}
 | 
						|
 | 
						|
NVImage*
 | 
						|
NVImage::AsNVImage()
 | 
						|
{
 | 
						|
  return this;
 | 
						|
};
 | 
						|
 | 
						|
bool
 | 
						|
NVImage::SetData(const Data& aData)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
 | 
						|
  MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
 | 
						|
 | 
						|
  // Calculate buffer size
 | 
						|
  // Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
 | 
						|
  const auto checkedSize =
 | 
						|
    CheckedInt<uint32_t>(aData.mYSize.height) * aData.mYStride +
 | 
						|
    CheckedInt<uint32_t>(aData.mCbCrSize.height) * aData.mCbCrStride;
 | 
						|
 | 
						|
  if (!checkedSize.isValid())
 | 
						|
    return false;
 | 
						|
 | 
						|
  const auto size = checkedSize.value();
 | 
						|
 | 
						|
  // Allocate a new buffer.
 | 
						|
  mBuffer = AllocateBuffer(size);
 | 
						|
  if (!mBuffer) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Update mBufferSize.
 | 
						|
  mBufferSize = size;
 | 
						|
 | 
						|
  // Update mData.
 | 
						|
  mData = aData;
 | 
						|
  mData.mYChannel = mBuffer.get();
 | 
						|
  mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
 | 
						|
  mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
 | 
						|
 | 
						|
  // Update mSize.
 | 
						|
  mSize = aData.mPicSize;
 | 
						|
 | 
						|
  // Copy the input data into mBuffer.
 | 
						|
  // This copies the y-channel and the interleaving CbCr-channel.
 | 
						|
  memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
const NVImage::Data*
 | 
						|
NVImage::GetData() const
 | 
						|
{
 | 
						|
  return &mData;
 | 
						|
}
 | 
						|
 | 
						|
UniquePtr<uint8_t>
 | 
						|
NVImage::AllocateBuffer(uint32_t aSize)
 | 
						|
{
 | 
						|
  UniquePtr<uint8_t> buffer(new uint8_t[aSize]);
 | 
						|
  return buffer;
 | 
						|
}
 | 
						|
 | 
						|
SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface)
 | 
						|
  : Image(nullptr, ImageFormat::CAIRO_SURFACE),
 | 
						|
    mSize(aSize),
 | 
						|
    mSourceSurface(aSourceSurface),
 | 
						|
    mTextureFlags(TextureFlags::DEFAULT)
 | 
						|
{}
 | 
						|
 | 
						|
SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
 | 
						|
  : Image(nullptr, ImageFormat::CAIRO_SURFACE),
 | 
						|
    mSize(aSourceSurface->GetSize()),
 | 
						|
    mSourceSurface(aSourceSurface),
 | 
						|
    mTextureFlags(TextureFlags::DEFAULT)
 | 
						|
{}
 | 
						|
 | 
						|
SourceSurfaceImage::~SourceSurfaceImage() = default;
 | 
						|
 | 
						|
TextureClient*
 | 
						|
SourceSurfaceImage::GetTextureClient(KnowsCompositor* aForwarder)
 | 
						|
{
 | 
						|
  if (!aForwarder) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  auto entry = mTextureClients.LookupForAdd(aForwarder->GetSerial());
 | 
						|
  if (entry) {
 | 
						|
    return entry.Data();
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<TextureClient> textureClient;
 | 
						|
  RefPtr<SourceSurface> surface = GetAsSourceSurface();
 | 
						|
  MOZ_ASSERT(surface);
 | 
						|
  if (surface) {
 | 
						|
    // gfx::BackendType::NONE means default to content backend
 | 
						|
    textureClient =
 | 
						|
      TextureClient::CreateFromSurface(aForwarder,
 | 
						|
                                       surface,
 | 
						|
                                       BackendSelector::Content,
 | 
						|
                                       mTextureFlags,
 | 
						|
                                       ALLOC_DEFAULT);
 | 
						|
  }
 | 
						|
  if (textureClient) {
 | 
						|
    textureClient->SyncWithObject(aForwarder->GetSyncObject());
 | 
						|
    entry.OrInsert([&textureClient](){ return textureClient; });
 | 
						|
    return textureClient;
 | 
						|
  }
 | 
						|
 | 
						|
  // Remove the speculatively added entry.
 | 
						|
  mTextureClients.Remove(aForwarder->GetSerial());
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
ImageContainer::ProducerID
 | 
						|
ImageContainer::AllocateProducerID()
 | 
						|
{
 | 
						|
  // Callable on all threads.
 | 
						|
  static Atomic<ImageContainer::ProducerID> sProducerID(0u);
 | 
						|
  return ++sProducerID;
 | 
						|
}
 | 
						|
 | 
						|
} // namespace layers
 | 
						|
} // namespace mozilla
 |