Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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/. */
#ifndef MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_
#define MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_
#include "base/process.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/Mutex.h"
#include "mozilla/ipc/SharedMemory.h"
#include "nsExpirationTracker.h"
namespace mozilla {
namespace gfx {
class SourceSurfaceSharedData;
/**
* This class is used to wrap shared (as in process) data buffers allocated by
* a SourceSurfaceSharedData object. It may live in the same process or a
* different process from the actual SourceSurfaceSharedData object.
*
* If it is in the same process, mBuf is the same object as that in the surface.
* It is a useful abstraction over just using the surface directly, because it
* can have a different lifetime from the surface; if the surface gets freed,
* consumers may continue accessing the data in the buffer. Releasing the
* original surface is a signal which feeds into SharedSurfacesParent to decide
* to release the SourceSurfaceSharedDataWrapper.
*
* If it is in a different process, mBuf is a new SharedMemory object which
* mapped in the given shared memory handle as read only memory.
*/
class SourceSurfaceSharedDataWrapper final : public DataSourceSurface {
typedef mozilla::ipc::SharedMemory SharedMemory;
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper,
override)
SourceSurfaceSharedDataWrapper()
: mStride(0),
mConsumers(0),
mFormat(SurfaceFormat::UNKNOWN),
mCreatorPid(0),
mCreatorRef(true) {}
void Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
SharedMemory::Handle aHandle, base::ProcessId aCreatorPid);
void Init(SourceSurfaceSharedData* aSurface);
base::ProcessId GetCreatorPid() const { return mCreatorPid; }
int32_t Stride() override { return mStride; }
SurfaceType GetType() const override {
return SurfaceType::DATA_SHARED_WRAPPER;
}
IntSize GetSize() const override { return mSize; }
SurfaceFormat GetFormat() const override { return mFormat; }
uint8_t* GetData() override { return static_cast<uint8_t*>(mBuf->Memory()); }
bool OnHeap() const override { return false; }
bool Map(MapType aMapType, MappedSurface* aMappedSurface) final;
void Unmap() final;
void ExpireMap();
bool AddConsumer() { return ++mConsumers == 1; }
bool RemoveConsumer(bool aForCreator) {
MOZ_ASSERT(mConsumers > 0);
if (aForCreator) {
if (!mCreatorRef) {
MOZ_ASSERT_UNREACHABLE("Already released creator reference!");
return false;
}
mCreatorRef = false;
}
return --mConsumers == 0;
}
uint32_t GetConsumers() const {
MOZ_ASSERT(mConsumers > 0);
return mConsumers;
}
bool HasCreatorRef() const { return mCreatorRef; }
nsExpirationState* GetExpirationState() { return &mExpirationState; }
private:
size_t GetDataLength() const {
return static_cast<size_t>(mStride) * mSize.height;
}
size_t GetAlignedDataLength() const {
return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
}
bool EnsureMapped(size_t aLength);
// Protects mapping and unmapping of mBuf.
Maybe<Mutex> mHandleLock;
nsExpirationState mExpirationState;
int32_t mStride;
uint32_t mConsumers;
IntSize mSize;
RefPtr<SharedMemory> mBuf;
SurfaceFormat mFormat;
base::ProcessId mCreatorPid;
bool mCreatorRef;
};
/**
* This class is used to wrap shared (as in process) data buffers used by a
* source surface.
*/
class SourceSurfaceSharedData : public DataSourceSurface {
typedef mozilla::ipc::SharedMemory SharedMemory;
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedData, override)
SourceSurfaceSharedData()
: mMutex("SourceSurfaceSharedData"),
mStride(0),
mHandleCount(0),
mFormat(SurfaceFormat::UNKNOWN),
mClosed(false),
mFinalized(false),
mShared(false) {}
/**
* Initialize the surface by creating a shared memory buffer with a size
* determined by aSize, aStride and aFormat. If aShare is true, it will also
* immediately attempt to share the surface with the GPU process via
* SharedSurfacesChild.
*/
bool Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
bool aShare = true);
uint8_t* GetData() final {
MutexAutoLock lock(mMutex);
return GetDataInternal();
}
int32_t Stride() final { return mStride; }
SurfaceType GetType() const override { return SurfaceType::DATA_SHARED; }
IntSize GetSize() const final { return mSize; }
SurfaceFormat GetFormat() const final { return mFormat; }
void SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
SizeOfInfo& aInfo) const final;
bool OnHeap() const final { return false; }
/**
* Although Map (and Moz2D in general) isn't normally threadsafe,
* we want to allow it for SourceSurfaceSharedData since it should
* always be fine (for reading at least).
*
* This is the same as the base class implementation except using
* mMapCount instead of mIsMapped since that breaks for multithread.
*
* Additionally if a reallocation happened while there were active
* mappings, then we guarantee that GetData will continue to return
* the same data pointer by retaining the old shared buffer until
* the last mapping is freed via Unmap.
*/
bool Map(MapType aMapType, MappedSurface* aMappedSurface) final {
MutexAutoLock lock(mMutex);
if (mFinalized && aMapType != MapType::READ) {
// Once finalized the data may be write-protected
return false;
}
++mMapCount;
aMappedSurface->mData = GetDataInternal();
aMappedSurface->mStride = mStride;
return true;
}
void Unmap() final {
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mMapCount > 0);
if (--mMapCount == 0) {
mOldBuf = nullptr;
}
}
/**
* Get a handle to share to another process for this buffer. Returns:
* NS_OK -- success, aHandle is valid.
* NS_ERROR_NOT_AVAILABLE -- handle was closed, need to reallocate.
* NS_ERROR_FAILURE -- failed to create a handle to share.
*/
nsresult CloneHandle(SharedMemory::Handle& aHandle);
/**
* Indicates the buffer is not expected to be shared with any more processes.
* May release the handle if possible (see CloseHandleInternal).
*/
void FinishedSharing() {
MutexAutoLock lock(mMutex);
mShared = true;
CloseHandleInternal();
}
/**
* Indicates whether or not the buffer can be shared with another process
* without reallocating. Note that this is racy and should only be used for
* informational/reporting purposes.
*/
bool CanShare() const {
MutexAutoLock lock(mMutex);
return !mClosed;
}
/**
* Allocate a new shared memory buffer so that we can get a new handle for
* sharing to new processes. CloneHandle must have failed with
* NS_ERROR_NOT_AVAILABLE in order for this to be safe to call. Returns true
* if the operation succeeds. If it fails, there is no state change.
*/
bool ReallocHandle();
/**
* Signals we have finished writing to the buffer and it may be marked as
* read only.
*/
void Finalize();
/**
* Indicates whether or not the buffer can change. If this returns true, it is
* guaranteed to continue to do so for the remainder of the surface's life.
*/
bool IsFinalized() const {
MutexAutoLock lock(mMutex);
return mFinalized;
}
/**
* Yields a dirty rect of what has changed since it was last called.
*/
Maybe<IntRect> TakeDirtyRect() final {
MutexAutoLock lock(mMutex);
if (mDirtyRect) {
Maybe<IntRect> ret = std::move(mDirtyRect);
return ret;
}
return Nothing();
}
/**
* Increment the invalidation counter.
*/
void Invalidate(const IntRect& aDirtyRect) final {
MutexAutoLock lock(mMutex);
if (!aDirtyRect.IsEmpty()) {
if (mDirtyRect) {
mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect);
} else {
mDirtyRect = Some(aDirtyRect);
}
} else {
mDirtyRect = Some(IntRect(IntPoint(0, 0), mSize));
}
MOZ_ASSERT_IF(mDirtyRect, !mDirtyRect->IsEmpty());
}
/**
* While a HandleLock exists for the given surface, the shared memory handle
* cannot be released.
*/
class MOZ_STACK_CLASS HandleLock final {
public:
explicit HandleLock(SourceSurfaceSharedData* aSurface)
: mSurface(aSurface) {
mSurface->LockHandle();
}
~HandleLock() { mSurface->UnlockHandle(); }
private:
RefPtr<SourceSurfaceSharedData> mSurface;
};
protected:
virtual ~SourceSurfaceSharedData() = default;
private:
friend class SourceSurfaceSharedDataWrapper;
void LockHandle() {
MutexAutoLock lock(mMutex);
++mHandleCount;
}
void UnlockHandle() {
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mHandleCount > 0);
--mHandleCount;
mShared = true;
CloseHandleInternal();
}
uint8_t* GetDataInternal() const;
size_t GetDataLength() const {
return static_cast<size_t>(mStride) * mSize.height;
}
size_t GetAlignedDataLength() const {
return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
}
/**
* Attempt to close the handle. Only if the buffer has been both finalized
* and we have completed sharing will it be released.
*/
void CloseHandleInternal();
mutable Mutex mMutex MOZ_UNANNOTATED;
int32_t mStride;
int32_t mHandleCount;
Maybe<IntRect> mDirtyRect;
IntSize mSize;
RefPtr<SharedMemory> mBuf;
RefPtr<SharedMemory> mOldBuf;
SurfaceFormat mFormat;
bool mClosed : 1;
bool mFinalized : 1;
bool mShared : 1;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ */