SourceSurfaceSharedData.h (10780B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ 8 #define MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ 9 10 #include "base/process.h" 11 #include "mozilla/gfx/2D.h" 12 #include "mozilla/Mutex.h" 13 #include "mozilla/ipc/SharedMemoryHandle.h" 14 #include "mozilla/ipc/SharedMemoryMapping.h" 15 #include "nsExpirationTracker.h" 16 17 namespace mozilla { 18 namespace gfx { 19 20 class SourceSurfaceSharedData; 21 22 /** 23 * This class is used to wrap shared (as in process) data buffers allocated by 24 * a SourceSurfaceSharedData object. It may live in the same process or a 25 * different process from the actual SourceSurfaceSharedData object. 26 * 27 * If it is in the same process, mBuf is the same object as that in the surface. 28 * It is a useful abstraction over just using the surface directly, because it 29 * can have a different lifetime from the surface; if the surface gets freed, 30 * consumers may continue accessing the data in the buffer. Releasing the 31 * original surface is a signal which feeds into SharedSurfacesParent to decide 32 * to release the SourceSurfaceSharedDataWrapper. 33 * 34 * If it is in a different process, mBuf is a new SharedMemory object which 35 * mapped in the given shared memory handle as read only memory. 36 */ 37 class SourceSurfaceSharedDataWrapper final : public DataSourceSurface { 38 public: 39 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper, 40 override) 41 42 SourceSurfaceSharedDataWrapper() = default; 43 44 void Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat, 45 mozilla::ipc::ReadOnlySharedMemoryHandle aHandle, 46 base::ProcessId aCreatorPid); 47 48 void Init(SourceSurfaceSharedData* aSurface); 49 50 base::ProcessId GetCreatorPid() const { return mCreatorPid; } 51 52 int32_t Stride() override { return mStride; } 53 54 SurfaceType GetType() const override { 55 return SurfaceType::DATA_SHARED_WRAPPER; 56 } 57 IntSize GetSize() const override { return mSize; } 58 SurfaceFormat GetFormat() const override { return mFormat; } 59 60 uint8_t* GetData() override { 61 // Cast away const-ness of shared memory to match the 62 // `DataSourceSurface::GetData` interface. The data will not be written to. 63 return mBuf ? const_cast<uint8_t*>(mBuf->DataAs<uint8_t>()) : nullptr; 64 } 65 66 bool OnHeap() const override { return false; } 67 68 bool Map(MapType aMapType, MappedSurface* aMappedSurface) final; 69 70 void Unmap() final; 71 72 void ExpireMap(); 73 74 bool AddConsumer() { return ++mConsumers == 1; } 75 76 bool RemoveConsumer(bool aForCreator) { 77 MOZ_ASSERT(mConsumers > 0); 78 if (aForCreator) { 79 if (!mCreatorRef) { 80 MOZ_ASSERT_UNREACHABLE("Already released creator reference!"); 81 return false; 82 } 83 mCreatorRef = false; 84 } 85 return --mConsumers == 0; 86 } 87 88 uint32_t GetConsumers() const { return mConsumers; } 89 90 bool HasCreatorRef() const { return mCreatorRef; } 91 92 nsExpirationState* GetExpirationState() { return &mExpirationState; } 93 94 private: 95 ~SourceSurfaceSharedDataWrapper() override { 96 MOZ_RELEASE_ASSERT(!mExpirationState.IsTracked()); 97 } 98 99 size_t GetDataLength() const { 100 return static_cast<size_t>(mStride) * mSize.height; 101 } 102 103 size_t GetAlignedDataLength() const { 104 return mozilla::ipc::shared_memory::PageAlignedSize(GetDataLength()); 105 } 106 107 bool EnsureMapped(size_t aLength); 108 109 // Protects mapping and unmapping of mBuf. 110 Maybe<Mutex> mHandleLock; 111 nsExpirationState mExpirationState; 112 int32_t mStride = 0; 113 uint32_t mConsumers = 1; 114 IntSize mSize; 115 // This is only used to support EnsureMapped if we fail initially. 116 mozilla::ipc::ReadOnlySharedMemoryHandle mBufHandle; 117 std::shared_ptr<mozilla::ipc::MutableOrReadOnlySharedMemoryMapping> mBuf; 118 SurfaceFormat mFormat = SurfaceFormat::UNKNOWN; 119 base::ProcessId mCreatorPid = 0; 120 bool mCreatorRef = true; 121 }; 122 123 /** 124 * This class is used to wrap shared (as in process) data buffers used by a 125 * source surface. 126 */ 127 class SourceSurfaceSharedData : public DataSourceSurface { 128 public: 129 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedData, override) 130 131 SourceSurfaceSharedData() 132 : mMutex("SourceSurfaceSharedData"), 133 mStride(0), 134 mHandleCount(0), 135 mFormat(SurfaceFormat::UNKNOWN), 136 mClosed(false), 137 mFinalized(false), 138 mShared(false) {} 139 140 /** 141 * Initialize the surface by creating a shared memory buffer with a size 142 * determined by aSize, aStride and aFormat. If aShare is true, it will also 143 * immediately attempt to share the surface with the GPU process via 144 * SharedSurfacesChild. 145 */ 146 bool Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat, 147 bool aShare = true); 148 149 uint8_t* GetData() final { 150 MutexAutoLock lock(mMutex); 151 return GetDataInternal(); 152 } 153 154 int32_t Stride() final { return mStride; } 155 156 SurfaceType GetType() const override { return SurfaceType::DATA_SHARED; } 157 IntSize GetSize() const final { return mSize; } 158 SurfaceFormat GetFormat() const final { return mFormat; } 159 160 void SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 161 SizeOfInfo& aInfo) const final; 162 163 bool OnHeap() const final { return false; } 164 165 /** 166 * Although Map (and Moz2D in general) isn't normally threadsafe, 167 * we want to allow it for SourceSurfaceSharedData since it should 168 * always be fine (for reading at least). 169 * 170 * This is the same as the base class implementation except using 171 * mMapCount instead of mIsMapped since that breaks for multithread. 172 * 173 * Additionally if a reallocation happened while there were active 174 * mappings, then we guarantee that GetData will continue to return 175 * the same data pointer by retaining the old shared buffer until 176 * the last mapping is freed via Unmap. 177 */ 178 bool Map(MapType aMapType, MappedSurface* aMappedSurface) final { 179 MutexAutoLock lock(mMutex); 180 if (mFinalized && aMapType != MapType::READ) { 181 // Once finalized the data may be write-protected 182 return false; 183 } 184 ++mMapCount; 185 aMappedSurface->mData = GetDataInternal(); 186 aMappedSurface->mStride = mStride; 187 return true; 188 } 189 190 void Unmap() final { 191 MutexAutoLock lock(mMutex); 192 MOZ_ASSERT(mMapCount > 0); 193 if (--mMapCount == 0) { 194 mOldBuf = nullptr; 195 } 196 } 197 198 /** 199 * Get a handle to share to another process for this buffer. Returns: 200 * NS_OK -- success, aHandle is valid. 201 * NS_ERROR_NOT_AVAILABLE -- handle was closed, need to reallocate. 202 * NS_ERROR_FAILURE -- failed to create a handle to share. 203 */ 204 nsresult CloneHandle(mozilla::ipc::ReadOnlySharedMemoryHandle& aHandle); 205 206 /** 207 * Indicates the buffer is not expected to be shared with any more processes. 208 * May release the handle if possible (see CloseHandleInternal). 209 */ 210 void FinishedSharing() { 211 MutexAutoLock lock(mMutex); 212 mShared = true; 213 CloseHandleInternal(); 214 } 215 216 /** 217 * Indicates whether or not the buffer can be shared with another process 218 * without reallocating. Note that this is racy and should only be used for 219 * informational/reporting purposes. 220 */ 221 bool CanShare() const { 222 MutexAutoLock lock(mMutex); 223 return !mClosed; 224 } 225 226 /** 227 * Allocate a new shared memory buffer so that we can get a new handle for 228 * sharing to new processes. CloneHandle must have failed with 229 * NS_ERROR_NOT_AVAILABLE in order for this to be safe to call. Returns true 230 * if the operation succeeds. If it fails, there is no state change. 231 */ 232 bool ReallocHandle(); 233 234 /** 235 * Signals we have finished writing to the buffer and it may be marked as 236 * read only. 237 */ 238 void Finalize(); 239 240 /** 241 * Indicates whether or not the buffer can change. If this returns true, it is 242 * guaranteed to continue to do so for the remainder of the surface's life. 243 */ 244 bool IsFinalized() const { 245 MutexAutoLock lock(mMutex); 246 return mFinalized; 247 } 248 249 /** 250 * Yields a dirty rect of what has changed since it was last called. 251 */ 252 Maybe<IntRect> TakeDirtyRect() final { 253 MutexAutoLock lock(mMutex); 254 if (mDirtyRect) { 255 Maybe<IntRect> ret = std::move(mDirtyRect); 256 return ret; 257 } 258 return Nothing(); 259 } 260 261 /** 262 * Increment the invalidation counter. 263 */ 264 void Invalidate(const IntRect& aDirtyRect) final { 265 MutexAutoLock lock(mMutex); 266 if (!aDirtyRect.IsEmpty()) { 267 if (mDirtyRect) { 268 mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect); 269 } else { 270 mDirtyRect = Some(aDirtyRect); 271 } 272 } else { 273 mDirtyRect = Some(IntRect(IntPoint(0, 0), mSize)); 274 } 275 MOZ_ASSERT_IF(mDirtyRect, !mDirtyRect->IsEmpty()); 276 } 277 278 /** 279 * While a HandleLock exists for the given surface, the shared memory handle 280 * cannot be released. 281 */ 282 class MOZ_STACK_CLASS HandleLock final { 283 public: 284 explicit HandleLock(SourceSurfaceSharedData* aSurface) 285 : mSurface(aSurface) { 286 mSurface->LockHandle(); 287 } 288 289 ~HandleLock() { mSurface->UnlockHandle(); } 290 291 private: 292 RefPtr<SourceSurfaceSharedData> mSurface; 293 }; 294 295 protected: 296 virtual ~SourceSurfaceSharedData() = default; 297 298 private: 299 friend class SourceSurfaceSharedDataWrapper; 300 301 void LockHandle() { 302 MutexAutoLock lock(mMutex); 303 ++mHandleCount; 304 } 305 306 void UnlockHandle() { 307 MutexAutoLock lock(mMutex); 308 MOZ_ASSERT(mHandleCount > 0); 309 --mHandleCount; 310 mShared = true; 311 CloseHandleInternal(); 312 } 313 314 uint8_t* GetDataInternal() const; 315 316 size_t GetDataLength() const { 317 return static_cast<size_t>(mStride) * mSize.height; 318 } 319 320 size_t GetAlignedDataLength() const { 321 return mozilla::ipc::shared_memory::PageAlignedSize(GetDataLength()); 322 } 323 324 /** 325 * Attempt to close the handle. Only if the buffer has been both finalized 326 * and we have completed sharing will it be released. 327 */ 328 void CloseHandleInternal(); 329 330 mutable Mutex mMutex MOZ_UNANNOTATED; 331 int32_t mStride; 332 int32_t mHandleCount; 333 Maybe<IntRect> mDirtyRect; 334 IntSize mSize; 335 mozilla::ipc::MutableSharedMemoryHandle mBufHandle; 336 // This class always has mutable mappings, however we need to share them with 337 // the wrapper which may be read-only, so we use MutableOrReadOnly. 338 std::shared_ptr<mozilla::ipc::MutableOrReadOnlySharedMemoryMapping> mBuf; 339 std::shared_ptr<mozilla::ipc::MutableOrReadOnlySharedMemoryMapping> mOldBuf; 340 SurfaceFormat mFormat; 341 bool mClosed : 1; 342 bool mFinalized : 1; 343 bool mShared : 1; 344 }; 345 346 } // namespace gfx 347 } // namespace mozilla 348 349 #endif /* MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ */