summaryrefslogtreecommitdiffstats
path: root/gfx/layers/SourceSurfaceSharedData.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/SourceSurfaceSharedData.h')
-rw-r--r--gfx/layers/SourceSurfaceSharedData.h329
1 files changed, 329 insertions, 0 deletions
diff --git a/gfx/layers/SourceSurfaceSharedData.h b/gfx/layers/SourceSurfaceSharedData.h
new file mode 100644
index 0000000000..53e910eb5d
--- /dev/null
+++ b/gfx/layers/SourceSurfaceSharedData.h
@@ -0,0 +1,329 @@
+/* -*- 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 "mozilla/gfx/2D.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/ipc/SharedMemoryBasic.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 SharedMemoryBasic object which
+ * mapped in the given shared memory handle as read only memory.
+ */
+class SourceSurfaceSharedDataWrapper final : public DataSourceSurface {
+ typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;
+
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper,
+ override)
+
+ SourceSurfaceSharedDataWrapper()
+ : mStride(0),
+ mConsumers(0),
+ mFormat(SurfaceFormat::UNKNOWN),
+ mCreatorPid(0),
+ mCreatorRef(true) {}
+
+ bool Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
+ const SharedMemoryBasic::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; }
+ 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 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; }
+
+ private:
+ size_t GetDataLength() const {
+ return static_cast<size_t>(mStride) * mSize.height;
+ }
+
+ size_t GetAlignedDataLength() const {
+ return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
+ }
+
+ int32_t mStride;
+ uint32_t mConsumers;
+ IntSize mSize;
+ RefPtr<SharedMemoryBasic> 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::SharedMemoryBasic SharedMemoryBasic;
+
+ 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 GuaranteePersistance() final;
+
+ 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, MappedSurface* aMappedSurface) final {
+ MutexAutoLock lock(mMutex);
+ ++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 ShareToProcess(base::ProcessId aPid,
+ SharedMemoryBasic::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. ShareToProcess 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;
+ int32_t mStride;
+ int32_t mHandleCount;
+ Maybe<IntRect> mDirtyRect;
+ IntSize mSize;
+ RefPtr<SharedMemoryBasic> mBuf;
+ RefPtr<SharedMemoryBasic> mOldBuf;
+ SurfaceFormat mFormat;
+ bool mClosed : 1;
+ bool mFinalized : 1;
+ bool mShared : 1;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ */