michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_IMAGECONTAINER_H michael@0: #define GFX_IMAGECONTAINER_H michael@0: michael@0: #include // for uint32_t, uint8_t, uint64_t michael@0: #include // for int32_t michael@0: #include "gfxTypes.h" michael@0: #include "ImageTypes.h" // for ImageFormat, etc michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT_HELPER2 michael@0: #include "mozilla/Mutex.h" // for Mutex michael@0: #include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc michael@0: #include "mozilla/TimeStamp.h" // for TimeStamp michael@0: #include "mozilla/gfx/Point.h" // For IntSize michael@0: #include "mozilla/layers/LayersTypes.h" // for LayersBackend, etc michael@0: #include "mozilla/mozalloc.h" // for operator delete, etc michael@0: #include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr, etc michael@0: #include "nsAutoRef.h" // for nsCountedRef michael@0: #include "nsCOMPtr.h" // for already_AddRefed michael@0: #include "nsDebug.h" // for NS_ASSERTION michael@0: #include "nsISupportsImpl.h" // for Image::Release, etc michael@0: #include "nsRect.h" // for nsIntRect michael@0: #include "nsSize.h" // for nsIntSize michael@0: #include "nsTArray.h" // for nsTArray michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/WeakPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "mozilla/EnumeratedArray.h" michael@0: michael@0: #ifndef XPCOM_GLUE_AVOID_NSPR michael@0: /** michael@0: * We need to be able to hold a reference to a Moz2D SourceSurface from Image michael@0: * subclasses. This is potentially a problem since Images can be addrefed michael@0: * or released off the main thread. We can ensure that we never AddRef michael@0: * a SourceSurface off the main thread, but we might want to Release due michael@0: * to an Image being destroyed off the main thread. michael@0: * michael@0: * We use nsCountedRef to reference the michael@0: * SourceSurface. When AddRefing, we assert that we're on the main thread. michael@0: * When Releasing, if we're not on the main thread, we post an event to michael@0: * the main thread to do the actual release. michael@0: */ michael@0: class nsMainThreadSourceSurfaceRef; michael@0: michael@0: template <> michael@0: class nsAutoRefTraits { michael@0: public: michael@0: typedef mozilla::gfx::SourceSurface* RawRef; michael@0: michael@0: /** michael@0: * The XPCOM event that will do the actual release on the main thread. michael@0: */ michael@0: class SurfaceReleaser : public nsRunnable { michael@0: public: michael@0: SurfaceReleaser(RawRef aRef) : mRef(aRef) {} michael@0: NS_IMETHOD Run() { michael@0: mRef->Release(); michael@0: return NS_OK; michael@0: } michael@0: RawRef mRef; michael@0: }; michael@0: michael@0: static RawRef Void() { return nullptr; } michael@0: static void Release(RawRef aRawRef) michael@0: { michael@0: if (NS_IsMainThread()) { michael@0: aRawRef->Release(); michael@0: return; michael@0: } michael@0: nsCOMPtr runnable = new SurfaceReleaser(aRawRef); michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: static void AddRef(RawRef aRawRef) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), michael@0: "Can only add a reference on the main thread"); michael@0: aRawRef->AddRef(); michael@0: } michael@0: }; michael@0: michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: struct ID3D10Texture2D; michael@0: struct ID3D10Device; michael@0: struct ID3D10ShaderResourceView; michael@0: #endif michael@0: michael@0: typedef void* HANDLE; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class CrossProcessMutex; michael@0: michael@0: namespace layers { michael@0: michael@0: class ImageClient; michael@0: class SharedPlanarYCbCrImage; michael@0: class TextureClient; michael@0: class CompositableClient; michael@0: class CompositableForwarder; michael@0: class SurfaceDescriptor; michael@0: michael@0: struct ImageBackendData michael@0: { michael@0: virtual ~ImageBackendData() {} michael@0: michael@0: protected: michael@0: ImageBackendData() {} michael@0: }; michael@0: michael@0: // sadly we'll need this until we get rid of Deprected image classes michael@0: class ISharedImage { michael@0: public: michael@0: virtual uint8_t* GetBuffer() = 0; michael@0: michael@0: /** michael@0: * For use with the CompositableClient only (so that the later can michael@0: * synchronize the TextureClient with the TextureHost). michael@0: */ michael@0: virtual TextureClient* GetTextureClient(CompositableClient* aClient) = 0; michael@0: }; michael@0: michael@0: /** michael@0: * A class representing a buffer of pixel data. The data can be in one michael@0: * of various formats including YCbCr. michael@0: * michael@0: * Create an image using an ImageContainer. Fill the image with data, and michael@0: * then call ImageContainer::SetImage to display it. An image must not be michael@0: * modified after calling SetImage. Image implementations do not need to michael@0: * perform locking; when filling an Image, the Image client is responsible michael@0: * for ensuring only one thread accesses the Image at a time, and after michael@0: * SetImage the image is immutable. michael@0: * michael@0: * When resampling an Image, only pixels within the buffer should be michael@0: * sampled. For example, cairo images should be sampled in EXTEND_PAD mode. michael@0: */ michael@0: class Image { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Image) michael@0: michael@0: public: michael@0: virtual ISharedImage* AsSharedImage() { return nullptr; } michael@0: michael@0: ImageFormat GetFormat() { return mFormat; } michael@0: void* GetImplData() { return mImplData; } michael@0: michael@0: virtual gfx::IntSize GetSize() = 0; michael@0: virtual nsIntRect GetPictureRect() michael@0: { michael@0: return nsIntRect(0, 0, GetSize().width, GetSize().height); michael@0: } michael@0: michael@0: ImageBackendData* GetBackendData(LayersBackend aBackend) michael@0: { return mBackendData[aBackend]; } michael@0: void SetBackendData(LayersBackend aBackend, ImageBackendData* aData) michael@0: { mBackendData[aBackend] = aData; } michael@0: michael@0: int32_t GetSerial() { return mSerial; } michael@0: michael@0: void MarkSent() { mSent = true; } michael@0: bool IsSentToCompositor() { return mSent; } michael@0: michael@0: virtual TemporaryRef GetAsSourceSurface() = 0; michael@0: michael@0: protected: michael@0: Image(void* aImplData, ImageFormat aFormat) : michael@0: mImplData(aImplData), michael@0: mSerial(++sSerialCounter), michael@0: mFormat(aFormat), michael@0: mSent(false) michael@0: {} michael@0: michael@0: // Protected destructor, to discourage deletion outside of Release(): michael@0: virtual ~Image() {} michael@0: michael@0: mozilla::EnumeratedArray> michael@0: mBackendData; michael@0: michael@0: void* mImplData; michael@0: int32_t mSerial; michael@0: ImageFormat mFormat; michael@0: static mozilla::Atomic sSerialCounter; michael@0: bool mSent; michael@0: }; michael@0: michael@0: /** michael@0: * A RecycleBin is owned by an ImageContainer. We store buffers in it that we michael@0: * want to recycle from one image to the next.It's a separate object from michael@0: * ImageContainer because images need to store a strong ref to their RecycleBin michael@0: * and we must avoid creating a reference loop between an ImageContainer and michael@0: * its active image. michael@0: */ michael@0: class BufferRecycleBin MOZ_FINAL { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin) michael@0: michael@0: //typedef mozilla::gl::GLContext GLContext; michael@0: michael@0: public: michael@0: BufferRecycleBin(); michael@0: michael@0: void RecycleBuffer(uint8_t* aBuffer, uint32_t aSize); michael@0: // Returns a recycled buffer of the right size, or allocates a new buffer. michael@0: uint8_t* GetBuffer(uint32_t aSize); michael@0: michael@0: private: michael@0: typedef mozilla::Mutex Mutex; michael@0: michael@0: // Private destructor, to discourage deletion outside of Release(): michael@0: ~BufferRecycleBin() michael@0: { michael@0: } michael@0: michael@0: // This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures michael@0: // and mRecycledTextureSizes michael@0: Mutex mLock; michael@0: michael@0: // We should probably do something to prune this list on a timer so we don't michael@0: // eat excess memory while video is paused... michael@0: nsTArray > mRecycledBuffers; michael@0: // This is only valid if mRecycledBuffers is non-empty michael@0: uint32_t mRecycledBufferSize; michael@0: }; michael@0: michael@0: class CompositionNotifySink michael@0: { michael@0: public: michael@0: virtual void DidComposite() = 0; michael@0: virtual ~CompositionNotifySink() {} michael@0: }; michael@0: michael@0: /** michael@0: * A class that manages Image creation for a LayerManager. The only reason michael@0: * we need a separate class here is that LayerManagers aren't threadsafe michael@0: * (because layers can only be used on the main thread) and we want to michael@0: * be able to create images from any thread, to facilitate video playback michael@0: * without involving the main thread, for example. michael@0: * Different layer managers can implement child classes of this making it michael@0: * possible to create layer manager specific images. michael@0: * This class is not meant to be used directly but rather can be set on an michael@0: * image container. This is usually done by the layer system internally and michael@0: * not explicitly by users. For PlanarYCbCr or Cairo images the default michael@0: * implementation will creates images whose data lives in system memory, for michael@0: * MacIOSurfaces the default implementation will be a simple MacIOSurface michael@0: * wrapper. michael@0: */ michael@0: michael@0: class ImageFactory michael@0: { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory) michael@0: protected: michael@0: friend class ImageContainer; michael@0: michael@0: ImageFactory() {} michael@0: virtual ~ImageFactory() {} michael@0: michael@0: virtual already_AddRefed CreateImage(ImageFormat aFormat, michael@0: const gfx::IntSize &aScaleHint, michael@0: BufferRecycleBin *aRecycleBin); michael@0: michael@0: }; michael@0: michael@0: /** michael@0: * This struct is used to store RemoteImages, it is meant to be able to live in michael@0: * shared memory. Therefor it should not contain a vtable pointer. Remote michael@0: * users can manipulate the data in this structure to specify what image is to michael@0: * be drawn by the container. When accessing this data users should make sure michael@0: * the mutex synchronizing access to the structure is held! michael@0: */ michael@0: struct RemoteImageData { michael@0: enum Type { michael@0: /** michael@0: * This is a format that uses raw bitmap data. michael@0: */ michael@0: RAW_BITMAP, michael@0: michael@0: /** michael@0: * This is a format that uses a pointer to a texture do draw directly michael@0: * from a shared texture. Any process may have created this texture handle, michael@0: * the process creating the texture handle is responsible for managing it's michael@0: * lifetime by managing the lifetime of the first D3D texture object this michael@0: * handle was created for. It must also ensure the handle is not set michael@0: * current anywhere when the last reference to this object is released. michael@0: */ michael@0: DXGI_TEXTURE_HANDLE michael@0: }; michael@0: /* These formats describe the format in the memory byte-order */ michael@0: enum Format { michael@0: /* 8 bits per channel */ michael@0: BGRA32, michael@0: /* 8 bits per channel, alpha channel is ignored */ michael@0: BGRX32 michael@0: }; michael@0: michael@0: // This should be set to true if a change was made so that the ImageContainer michael@0: // knows to throw out any cached RemoteImage objects. michael@0: bool mWasUpdated; michael@0: Type mType; michael@0: Format mFormat; michael@0: gfx::IntSize mSize; michael@0: union { michael@0: struct { michael@0: /* This pointer is set by a remote process, however it will be set to michael@0: * the container process' address the memory of the raw bitmap resides michael@0: * at. michael@0: */ michael@0: unsigned char *mData; michael@0: int mStride; michael@0: } mBitmap; michael@0: #ifdef XP_WIN michael@0: HANDLE mTextureHandle; michael@0: #endif michael@0: }; michael@0: }; michael@0: michael@0: /** michael@0: * A class that manages Images for an ImageLayer. The only reason michael@0: * we need a separate class here is that ImageLayers aren't threadsafe michael@0: * (because layers can only be used on the main thread) and we want to michael@0: * be able to set the current Image from any thread, to facilitate michael@0: * video playback without involving the main thread, for example. michael@0: * michael@0: * An ImageContainer can operate in one of three modes: michael@0: * 1) Normal. Triggered by constructing the ImageContainer with michael@0: * DISABLE_ASYNC or when compositing is happening on the main thread. michael@0: * SetCurrentImage changes ImageContainer state but nothing is sent to the michael@0: * compositor until the next layer transaction. michael@0: * 2) Asynchronous. Initiated by constructing the ImageContainer with michael@0: * ENABLE_ASYNC when compositing is happening on the main thread. michael@0: * SetCurrentImage sends a message through the ImageBridge to the compositor michael@0: * thread to update the image, without going through the main thread or michael@0: * a layer transaction. michael@0: * 3) Remote. Initiated by calling SetRemoteImageData on the ImageContainer michael@0: * before any other activity. michael@0: * The ImageContainer uses a shared memory block containing a cross-process mutex michael@0: * to communicate with the compositor thread. SetCurrentImage synchronously michael@0: * updates the shared state to point to the new image and the old image michael@0: * is immediately released (not true in Normal or Asynchronous modes). michael@0: */ michael@0: class ImageContainer MOZ_FINAL : public SupportsWeakPtr { michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer) michael@0: public: michael@0: MOZ_DECLARE_REFCOUNTED_TYPENAME(ImageContainer) michael@0: michael@0: enum { DISABLE_ASYNC = 0x0, ENABLE_ASYNC = 0x01 }; michael@0: michael@0: ImageContainer(int flag = 0); michael@0: michael@0: /** michael@0: * Create an Image in one of the given formats. michael@0: * Picks the "best" format from the list and creates an Image of that michael@0: * format. michael@0: * Returns null if this backend does not support any of the formats. michael@0: * Can be called on any thread. This method takes mReentrantMonitor michael@0: * when accessing thread-shared state. michael@0: */ michael@0: already_AddRefed CreateImage(ImageFormat aFormat); michael@0: michael@0: /** michael@0: * Set an Image as the current image to display. The Image must have michael@0: * been created by this ImageContainer. michael@0: * Can be called on any thread. This method takes mReentrantMonitor michael@0: * when accessing thread-shared state. michael@0: * aImage can be null. While it's null, nothing will be painted. michael@0: * michael@0: * The Image data must not be modified after this method is called! michael@0: * Note that this must not be called if ENABLE_ASYNC has not been set. michael@0: * michael@0: * Implementations must call CurrentImageChanged() while holding michael@0: * mReentrantMonitor. michael@0: * michael@0: * If this ImageContainer has an ImageClient for async video: michael@0: * Schelude a task to send the image to the compositor using the michael@0: * PImageBridge protcol without using the main thread. michael@0: */ michael@0: void SetCurrentImage(Image* aImage); michael@0: michael@0: /** michael@0: * Clear all images. Let ImageClient release all TextureClients. michael@0: */ michael@0: void ClearAllImages(); michael@0: michael@0: /** michael@0: * Clear all images except current one. michael@0: * Let ImageClient release all TextureClients except front one. michael@0: */ michael@0: void ClearAllImagesExceptFront(); michael@0: michael@0: /** michael@0: * Clear the current image. michael@0: * This function is expect to be called only from a CompositableClient michael@0: * that belongs to ImageBridgeChild. Created to prevent dead lock. michael@0: * See Bug 901224. michael@0: */ michael@0: void ClearCurrentImage(); michael@0: michael@0: /** michael@0: * Set an Image as the current image to display. The Image must have michael@0: * been created by this ImageContainer. michael@0: * Must be called on the main thread, within a layers transaction. michael@0: * michael@0: * This method takes mReentrantMonitor michael@0: * when accessing thread-shared state. michael@0: * aImage can be null. While it's null, nothing will be painted. michael@0: * michael@0: * The Image data must not be modified after this method is called! michael@0: * Note that this must not be called if ENABLE_ASYNC been set. michael@0: * michael@0: * Implementations must call CurrentImageChanged() while holding michael@0: * mReentrantMonitor. michael@0: */ michael@0: void SetCurrentImageInTransaction(Image* aImage); michael@0: michael@0: /** michael@0: * Returns true if this ImageContainer uses the ImageBridge IPDL protocol. michael@0: * michael@0: * Can be called from any thread. michael@0: */ michael@0: bool IsAsync() const; michael@0: michael@0: /** michael@0: * If this ImageContainer uses ImageBridge, returns the ID associated to michael@0: * this container, for use in the ImageBridge protocol. michael@0: * Returns 0 if this ImageContainer does not use ImageBridge. Note that michael@0: * 0 is always an invalid ID for asynchronous image containers. michael@0: * michael@0: * Can be called from any thread. michael@0: */ michael@0: uint64_t GetAsyncContainerID() const; michael@0: michael@0: /** michael@0: * Returns if the container currently has an image. michael@0: * Can be called on any thread. This method takes mReentrantMonitor michael@0: * when accessing thread-shared state. michael@0: */ michael@0: bool HasCurrentImage(); michael@0: michael@0: /** michael@0: * Lock the current Image. michael@0: * This has to add a reference since otherwise there are race conditions michael@0: * where the current image is destroyed before the caller can add michael@0: * a reference. This lock strictly guarantees the underlying image remains michael@0: * valid, it does not mean the current image cannot change. michael@0: * Can be called on any thread. This method will lock the cross-process michael@0: * mutex to ensure remote processes cannot alter underlying data. This call michael@0: * -must- be balanced by a call to UnlockCurrentImage and users should avoid michael@0: * holding the image locked for a long time. michael@0: */ michael@0: already_AddRefed LockCurrentImage(); michael@0: michael@0: /** michael@0: * This call unlocks the image. For remote images releasing the cross-process michael@0: * mutex. michael@0: */ michael@0: void UnlockCurrentImage(); michael@0: michael@0: /** michael@0: * Get the current image as a SourceSurface. This is useful for fallback michael@0: * rendering. michael@0: * This can only be called from the main thread, since cairo objects michael@0: * can only be used from the main thread. michael@0: * This is defined here and not on Image because it's possible (likely) michael@0: * that some backends will make an Image "ready to draw" only when it michael@0: * becomes the current image for an image container. michael@0: * Returns null if there is no current image. michael@0: * Returns the size in aSize. michael@0: * The returned surface will never be modified. The caller must not michael@0: * modify it. michael@0: * Can be called on any thread. This method takes mReentrantMonitor michael@0: * when accessing thread-shared state. michael@0: * If the current image is a remote image, that is, if it is an image that michael@0: * may be shared accross processes, calling this function will make michael@0: * a copy of the image data while holding the mRemoteDataMutex. If possible, michael@0: * the lock methods should be used to avoid the copy, however this should be michael@0: * avoided if the surface is required for a long period of time. michael@0: */ michael@0: TemporaryRef GetCurrentAsSourceSurface(gfx::IntSize* aSizeResult); michael@0: michael@0: /** michael@0: * Same as LockCurrentAsSurface but for Moz2D michael@0: */ michael@0: TemporaryRef LockCurrentAsSourceSurface(gfx::IntSize* aSizeResult, michael@0: Image** aCurrentImage = nullptr); michael@0: michael@0: /** michael@0: * Returns the size of the image in pixels. michael@0: * Can be called on any thread. This method takes mReentrantMonitor when accessing michael@0: * thread-shared state. michael@0: */ michael@0: gfx::IntSize GetCurrentSize(); michael@0: michael@0: /** michael@0: * Sets a size that the image is expected to be rendered at. michael@0: * This is a hint for image backends to optimize scaling. michael@0: * Default implementation in this class is to ignore the hint. michael@0: * Can be called on any thread. This method takes mReentrantMonitor michael@0: * when accessing thread-shared state. michael@0: */ michael@0: void SetScaleHint(const gfx::IntSize& aScaleHint) michael@0: { mScaleHint = aScaleHint; } michael@0: michael@0: void SetImageFactory(ImageFactory *aFactory) michael@0: { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: mImageFactory = aFactory ? aFactory : new ImageFactory(); michael@0: } michael@0: michael@0: /** michael@0: * Returns the time at which the currently contained image was first michael@0: * painted. This is reset every time a new image is set as the current michael@0: * image. Note this may return a null timestamp if the current image michael@0: * has not yet been painted. Can be called from any thread. michael@0: */ michael@0: TimeStamp GetPaintTime() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return mPaintTime; michael@0: } michael@0: michael@0: /** michael@0: * Returns the number of images which have been contained in this container michael@0: * and painted at least once. Can be called from any thread. michael@0: */ michael@0: uint32_t GetPaintCount() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: return mPaintCount; michael@0: } michael@0: michael@0: /** michael@0: * Resets the paint count to zero. michael@0: * Can be called from any thread. michael@0: */ michael@0: void ResetPaintCount() { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: mPaintCount = 0; michael@0: } michael@0: michael@0: /** michael@0: * Increments mPaintCount if this is the first time aPainted has been michael@0: * painted, and sets mPaintTime if the painted image is the current image. michael@0: * current image. Can be called from any thread. michael@0: */ michael@0: void NotifyPaintedImage(Image* aPainted) { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: michael@0: nsRefPtr current = mActiveImage; michael@0: if (aPainted == current) { michael@0: if (mPaintTime.IsNull()) { michael@0: mPaintTime = TimeStamp::Now(); michael@0: mPaintCount++; michael@0: } michael@0: } else if (!mPreviousImagePainted) { michael@0: // While we were painting this image, the current image changed. We michael@0: // still must count it as painted, but can't set mPaintTime, since we're michael@0: // no longer the current image. michael@0: mPaintCount++; michael@0: mPreviousImagePainted = true; michael@0: } michael@0: michael@0: if (mCompositionNotifySink) { michael@0: mCompositionNotifySink->DidComposite(); michael@0: } michael@0: } michael@0: michael@0: void SetCompositionNotifySink(CompositionNotifySink *aSink) { michael@0: mCompositionNotifySink = aSink; michael@0: } michael@0: michael@0: /** michael@0: * This function is called to tell the ImageContainer where the michael@0: * (cross-process) segment lives where the shared data about possible michael@0: * remote images are stored. In addition to this a CrossProcessMutex object michael@0: * is passed telling the container how to synchronize access to this data. michael@0: * NOTE: This should be called during setup of the container and not after michael@0: * usage has started. michael@0: */ michael@0: void SetRemoteImageData(RemoteImageData *aRemoteData, michael@0: CrossProcessMutex *aRemoteDataMutex); michael@0: /** michael@0: * This can be used to check if the container has RemoteData set. michael@0: */ michael@0: RemoteImageData *GetRemoteImageData() { return mRemoteData; } michael@0: michael@0: private: michael@0: typedef mozilla::ReentrantMonitor ReentrantMonitor; michael@0: michael@0: // Private destructor, to discourage deletion outside of Release(): michael@0: ~ImageContainer(); michael@0: michael@0: void SetCurrentImageInternal(Image* aImage); michael@0: michael@0: // This is called to ensure we have an active image, this may not be true michael@0: // when we're storing image information in a RemoteImageData structure. michael@0: // NOTE: If we have remote data mRemoteDataMutex should be locked when michael@0: // calling this function! michael@0: void EnsureActiveImage(); michael@0: michael@0: // ReentrantMonitor to protect thread safe access to the "current michael@0: // image", and any other state which is shared between threads. michael@0: ReentrantMonitor mReentrantMonitor; michael@0: michael@0: // Performs necessary housekeeping to ensure the painted frame statistics michael@0: // are accurate. Must be called by SetCurrentImage() implementations with michael@0: // mReentrantMonitor held. michael@0: void CurrentImageChanged() { michael@0: mReentrantMonitor.AssertCurrentThreadIn(); michael@0: mPreviousImagePainted = !mPaintTime.IsNull(); michael@0: mPaintTime = TimeStamp(); michael@0: } michael@0: michael@0: nsRefPtr mActiveImage; michael@0: michael@0: // Number of contained images that have been painted at least once. It's up michael@0: // to the ImageContainer implementation to ensure accesses to this are michael@0: // threadsafe. michael@0: uint32_t mPaintCount; michael@0: michael@0: // Time stamp at which the current image was first painted. It's up to the michael@0: // ImageContainer implementation to ensure accesses to this are threadsafe. michael@0: TimeStamp mPaintTime; michael@0: michael@0: // Denotes whether the previous image was painted. michael@0: bool mPreviousImagePainted; michael@0: michael@0: // This is the image factory used by this container, layer managers using michael@0: // this container can set an alternative image factory that will be used to michael@0: // create images for this container. michael@0: nsRefPtr mImageFactory; michael@0: michael@0: gfx::IntSize mScaleHint; michael@0: michael@0: nsRefPtr mRecycleBin; michael@0: michael@0: // This contains the remote image data for this container, if this is nullptr michael@0: // that means the container has no other process that may control its active michael@0: // image. michael@0: RemoteImageData *mRemoteData; michael@0: michael@0: // This cross-process mutex is used to synchronise access to mRemoteData. michael@0: // When this mutex is held, we will always be inside the mReentrantMonitor michael@0: // however the same is not true vice versa. michael@0: CrossProcessMutex *mRemoteDataMutex; michael@0: michael@0: CompositionNotifySink *mCompositionNotifySink; michael@0: michael@0: // This member points to an ImageClient if this ImageContainer was michael@0: // sucessfully created with ENABLE_ASYNC, or points to null otherwise. michael@0: // 'unsuccessful' in this case only means that the ImageClient could not michael@0: // be created, most likely because off-main-thread compositing is not enabled. michael@0: // In this case the ImageContainer is perfectly usable, but it will forward michael@0: // frames to the compositor through transactions in the main thread rather than michael@0: // asynchronusly using the ImageBridge IPDL protocol. michael@0: ImageClient* mImageClient; michael@0: }; michael@0: michael@0: class AutoLockImage michael@0: { michael@0: public: michael@0: AutoLockImage(ImageContainer *aContainer) : mContainer(aContainer) { mImage = mContainer->LockCurrentImage(); } michael@0: AutoLockImage(ImageContainer *aContainer, RefPtr *aSurface) : mContainer(aContainer) { michael@0: *aSurface = mContainer->LockCurrentAsSourceSurface(&mSize, getter_AddRefs(mImage)); michael@0: } michael@0: ~AutoLockImage() { if (mContainer) { mContainer->UnlockCurrentImage(); } } michael@0: michael@0: Image* GetImage() { return mImage; } michael@0: const gfx::IntSize &GetSize() { return mSize; } michael@0: michael@0: void Unlock() { michael@0: if (mContainer) { michael@0: mImage = nullptr; michael@0: mContainer->UnlockCurrentImage(); michael@0: mContainer = nullptr; michael@0: } michael@0: } michael@0: michael@0: /** Things get a little tricky here, because our underlying image can -still- michael@0: * change, and OS X requires a complicated callback mechanism to update this michael@0: * we need to support staying the lock and getting the new image in a proper michael@0: * way. This method makes any images retrieved with GetImage invalid! michael@0: */ michael@0: void Refresh() { michael@0: if (mContainer) { michael@0: mContainer->UnlockCurrentImage(); michael@0: mImage = mContainer->LockCurrentImage(); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: ImageContainer *mContainer; michael@0: nsRefPtr mImage; michael@0: gfx::IntSize mSize; michael@0: }; michael@0: michael@0: struct PlanarYCbCrData { michael@0: // Luminance buffer michael@0: uint8_t* mYChannel; michael@0: int32_t mYStride; michael@0: gfx::IntSize mYSize; michael@0: int32_t mYSkip; michael@0: // Chroma buffers michael@0: uint8_t* mCbChannel; michael@0: uint8_t* mCrChannel; michael@0: int32_t mCbCrStride; michael@0: gfx::IntSize mCbCrSize; michael@0: int32_t mCbSkip; michael@0: int32_t mCrSkip; michael@0: // Picture region michael@0: uint32_t mPicX; michael@0: uint32_t mPicY; michael@0: gfx::IntSize mPicSize; michael@0: StereoMode mStereoMode; michael@0: michael@0: nsIntRect GetPictureRect() const { michael@0: return nsIntRect(mPicX, mPicY, michael@0: mPicSize.width, michael@0: mPicSize.height); michael@0: } michael@0: michael@0: PlanarYCbCrData() michael@0: : mYChannel(nullptr), mYStride(0), mYSize(0, 0), mYSkip(0) michael@0: , mCbChannel(nullptr), mCrChannel(nullptr) michael@0: , mCbCrStride(0), mCbCrSize(0, 0) , mCbSkip(0), mCrSkip(0) michael@0: , mPicX(0), mPicY(0), mPicSize(0, 0), mStereoMode(StereoMode::MONO) michael@0: {} michael@0: }; michael@0: michael@0: /****** Image subtypes for the different formats ******/ michael@0: michael@0: /** michael@0: * We assume that the image data is in the REC 470M color space (see michael@0: * Theora specification, section 4.3.1). michael@0: * michael@0: * The YCbCr format can be: michael@0: * michael@0: * 4:4:4 - CbCr width/height are the same as Y. michael@0: * 4:2:2 - CbCr width is half that of Y. Height is the same. michael@0: * 4:2:0 - CbCr width and height is half that of Y. michael@0: * michael@0: * The color format is detected based on the height/width ratios michael@0: * defined above. michael@0: * michael@0: * The Image that is rendered is the picture region defined by michael@0: * mPicX, mPicY and mPicSize. The size of the rendered image is michael@0: * mPicSize, not mYSize or mCbCrSize. michael@0: * michael@0: * mYSkip, mCbSkip, mCrSkip are added to support various output michael@0: * formats from hardware decoder. They are per-pixel skips in the michael@0: * source image. michael@0: * michael@0: * For example when image width is 640, mYStride is 670, mYSkip is 3, michael@0: * the mYChannel buffer looks like: michael@0: * michael@0: * |<----------------------- mYStride ----------------------------->| michael@0: * |<----------------- mYSize.width --------------->| michael@0: * 0 3 6 9 12 15 18 21 659 669 michael@0: * |----------------------------------------------------------------| michael@0: * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%| michael@0: * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%| michael@0: * |Y___Y___Y___Y___Y___Y___Y___Y... |%%%%%%%%%| michael@0: * | |<->| michael@0: * mYSkip michael@0: */ michael@0: class PlanarYCbCrImage : public Image { michael@0: public: michael@0: typedef PlanarYCbCrData Data; michael@0: michael@0: enum { michael@0: MAX_DIMENSION = 16384 michael@0: }; michael@0: michael@0: virtual ~PlanarYCbCrImage(); michael@0: michael@0: /** michael@0: * This makes a copy of the data buffers, in order to support functioning michael@0: * in all different layer managers. michael@0: */ michael@0: virtual void SetData(const Data& aData); michael@0: michael@0: /** michael@0: * This doesn't make a copy of the data buffers. Can be used when mBuffer is michael@0: * pre allocated with AllocateAndGetNewBuffer(size) and then SetDataNoCopy is michael@0: * called to only update the picture size, planes etc. fields in mData. michael@0: * The GStreamer media backend uses this to decode into PlanarYCbCrImage(s) michael@0: * directly. michael@0: */ michael@0: virtual void SetDataNoCopy(const Data &aData); michael@0: michael@0: /** michael@0: * This allocates and returns a new buffer michael@0: */ michael@0: virtual uint8_t* AllocateAndGetNewBuffer(uint32_t aSize); michael@0: michael@0: /** michael@0: * Ask this Image to not convert YUV to RGB during SetData, and make michael@0: * the original data available through GetData. This is optional, michael@0: * and not all PlanarYCbCrImages will support it. michael@0: */ michael@0: virtual void SetDelayedConversion(bool aDelayed) { } michael@0: michael@0: /** michael@0: * Grab the original YUV data. This is optional. michael@0: */ michael@0: virtual const Data* GetData() { return &mData; } michael@0: michael@0: /** michael@0: * Return the number of bytes of heap memory used to store this image. michael@0: */ michael@0: virtual uint32_t GetDataSize() { return mBufferSize; } michael@0: michael@0: virtual bool IsValid() { return !!mBufferSize; } michael@0: michael@0: virtual gfx::IntSize GetSize() { return mSize; } michael@0: michael@0: PlanarYCbCrImage(BufferRecycleBin *aRecycleBin); michael@0: michael@0: virtual SharedPlanarYCbCrImage *AsSharedPlanarYCbCrImage() { return nullptr; } michael@0: michael@0: virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: protected: michael@0: /** michael@0: * Make a copy of the YCbCr data into local storage. michael@0: * michael@0: * @param aData Input image data. michael@0: */ michael@0: void CopyData(const Data& aData); michael@0: michael@0: /** michael@0: * Return a buffer to store image data in. michael@0: * The default implementation returns memory that can michael@0: * be freed wit delete[] michael@0: */ michael@0: virtual uint8_t* AllocateBuffer(uint32_t aSize); michael@0: michael@0: TemporaryRef GetAsSourceSurface(); michael@0: michael@0: void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; } michael@0: gfxImageFormat GetOffscreenFormat(); michael@0: michael@0: nsAutoArrayPtr mBuffer; michael@0: uint32_t mBufferSize; michael@0: Data mData; michael@0: gfx::IntSize mSize; michael@0: gfxImageFormat mOffscreenFormat; michael@0: nsCountedRef mSourceSurface; michael@0: nsRefPtr mRecycleBin; michael@0: }; michael@0: michael@0: /** michael@0: * Currently, the data in a CairoImage surface is treated as being in the michael@0: * device output color space. This class is very simple as all backends michael@0: * have to know about how to deal with drawing a cairo image. michael@0: */ michael@0: class CairoImage : public Image, michael@0: public ISharedImage { michael@0: public: michael@0: struct Data { michael@0: gfx::IntSize mSize; michael@0: RefPtr mSourceSurface; michael@0: }; michael@0: michael@0: /** michael@0: * This can only be called on the main thread. It may add a reference michael@0: * to the surface (which will eventually be released on the main thread). michael@0: * The surface must not be modified after this call!!! michael@0: */ michael@0: void SetData(const Data& aData) michael@0: { michael@0: mSize = aData.mSize; michael@0: mSourceSurface = aData.mSourceSurface; michael@0: } michael@0: michael@0: virtual TemporaryRef GetAsSourceSurface() michael@0: { michael@0: return mSourceSurface.get(); michael@0: } michael@0: michael@0: virtual ISharedImage* AsSharedImage() { return this; } michael@0: virtual uint8_t* GetBuffer() { return nullptr; } michael@0: virtual TextureClient* GetTextureClient(CompositableClient* aClient); michael@0: michael@0: gfx::IntSize GetSize() { return mSize; } michael@0: michael@0: CairoImage(); michael@0: ~CairoImage(); michael@0: michael@0: gfx::IntSize mSize; michael@0: michael@0: nsCountedRef mSourceSurface; michael@0: nsDataHashtable > mTextureClients; michael@0: }; michael@0: michael@0: class RemoteBitmapImage : public Image { michael@0: public: michael@0: RemoteBitmapImage() : Image(nullptr, ImageFormat::REMOTE_IMAGE_BITMAP) {} michael@0: michael@0: TemporaryRef GetAsSourceSurface(); michael@0: michael@0: gfx::IntSize GetSize() { return mSize; } michael@0: michael@0: unsigned char *mData; michael@0: int mStride; michael@0: gfx::IntSize mSize; michael@0: RemoteImageData::Format mFormat; michael@0: }; michael@0: michael@0: } //namespace michael@0: } //namespace michael@0: michael@0: #endif