SourceSurfaceSharedData.cpp (8600B)
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 #include "SourceSurfaceSharedData.h" 8 9 #include "mozilla/Likely.h" 10 #include "mozilla/StaticPrefs_image.h" 11 #include "mozilla/ipc/SharedMemoryMapping.h" 12 #include "mozilla/layers/SharedSurfacesChild.h" 13 #include "mozilla/layers/SharedSurfacesParent.h" 14 #include "nsDebug.h" // for NS_ABORT_OOM 15 16 #include "base/process_util.h" 17 18 #ifdef DEBUG 19 /** 20 * If defined, this makes SourceSurfaceSharedData::Finalize memory protect the 21 * underlying shared buffer in the producing process (the content or UI 22 * process). Given flushing the page table is expensive, and its utility is 23 * predominantly diagnostic (in case of overrun), turn it off by default. 24 */ 25 # define SHARED_SURFACE_PROTECT_FINALIZED 26 #endif 27 28 using namespace mozilla::layers; 29 30 namespace mozilla { 31 namespace gfx { 32 33 void SourceSurfaceSharedDataWrapper::Init( 34 const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat, 35 ipc::ReadOnlySharedMemoryHandle aHandle, base::ProcessId aCreatorPid) { 36 MOZ_ASSERT(!mBuf); 37 mSize = aSize; 38 mStride = aStride; 39 mFormat = aFormat; 40 mCreatorPid = aCreatorPid; 41 42 size_t len = GetAlignedDataLength(); 43 mBufHandle = std::move(aHandle); 44 if (!mBufHandle) { 45 MOZ_CRASH("Invalid shared memory handle!"); 46 } 47 48 bool mapped = EnsureMapped(len); 49 if ((sizeof(uintptr_t) <= 4 || 50 StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) && 51 len / 1024 > 52 StaticPrefs::image_mem_shared_unmap_min_threshold_kb_AtStartup()) { 53 mHandleLock.emplace("SourceSurfaceSharedDataWrapper::mHandleLock"); 54 55 if (mapped) { 56 // Tracking at the initial mapping, and not just after the first use of 57 // the surface means we might get unmapped again before the next frame 58 // gets rendered if a low virtual memory condition persists. 59 SharedSurfacesParent::AddTracking(this); 60 } 61 } else if (!mapped) { 62 // We don't support unmapping for this surface, and we failed to map it. 63 NS_ABORT_OOM(len); 64 } else { 65 mBufHandle = nullptr; 66 } 67 } 68 69 void SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface) { 70 MOZ_ASSERT(!mBuf); 71 MOZ_ASSERT(aSurface); 72 mSize = aSurface->mSize; 73 mStride = aSurface->mStride; 74 mFormat = aSurface->mFormat; 75 mCreatorPid = base::GetCurrentProcId(); 76 mBuf = aSurface->mBuf; 77 } 78 79 bool SourceSurfaceSharedDataWrapper::EnsureMapped(size_t aLength) { 80 MOZ_ASSERT(!GetData()); 81 82 if (mBufHandle.Size() < aLength) { 83 return false; 84 } 85 86 auto mapping = mBufHandle.Map(); 87 while (!mapping) { 88 nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired; 89 if (!SharedSurfacesParent::AgeOneGeneration(expired)) { 90 return false; 91 } 92 MOZ_ASSERT(!expired.Contains(this)); 93 SharedSurfacesParent::ExpireMap(expired); 94 mapping = mBufHandle.Map(); 95 } 96 97 mBuf = std::make_shared<ipc::MutableOrReadOnlySharedMemoryMapping>( 98 std::move(mapping)); 99 100 return true; 101 } 102 103 bool SourceSurfaceSharedDataWrapper::Map(MapType aMapType, 104 MappedSurface* aMappedSurface) { 105 uint8_t* dataPtr; 106 107 if (aMapType != MapType::READ) { 108 // The data may be write-protected 109 return false; 110 } 111 112 if (mHandleLock) { 113 MutexAutoLock lock(*mHandleLock); 114 dataPtr = GetData(); 115 if (mMapCount == 0) { 116 if (mConsumers > 0) { 117 SharedSurfacesParent::RemoveTracking(this); 118 } 119 if (!dataPtr) { 120 size_t len = GetAlignedDataLength(); 121 if (!EnsureMapped(len)) { 122 NS_ABORT_OOM(len); 123 } 124 dataPtr = GetData(); 125 } 126 } 127 ++mMapCount; 128 } else { 129 dataPtr = GetData(); 130 ++mMapCount; 131 } 132 133 MOZ_ASSERT(dataPtr); 134 aMappedSurface->mData = dataPtr; 135 aMappedSurface->mStride = mStride; 136 return true; 137 } 138 139 void SourceSurfaceSharedDataWrapper::Unmap() { 140 if (mHandleLock) { 141 MutexAutoLock lock(*mHandleLock); 142 if (--mMapCount == 0 && mConsumers > 0) { 143 SharedSurfacesParent::AddTracking(this); 144 } 145 } else { 146 --mMapCount; 147 } 148 MOZ_ASSERT(mMapCount >= 0); 149 } 150 151 void SourceSurfaceSharedDataWrapper::ExpireMap() { 152 MutexAutoLock lock(*mHandleLock); 153 if (mMapCount == 0) { 154 // This unmaps the stored memory mapping. 155 *mBuf = nullptr; 156 } 157 } 158 159 bool SourceSurfaceSharedData::Init(const IntSize& aSize, int32_t aStride, 160 SurfaceFormat aFormat, 161 bool aShare /* = true */) { 162 mSize = aSize; 163 mStride = aStride; 164 mFormat = aFormat; 165 166 size_t len = GetAlignedDataLength(); 167 mBufHandle = ipc::shared_memory::Create(len); 168 mBuf = std::make_shared<ipc::MutableOrReadOnlySharedMemoryMapping>( 169 mBufHandle.Map()); 170 if (NS_WARN_IF(!mBufHandle) || NS_WARN_IF(!mBuf || !*mBuf)) { 171 return false; 172 } 173 174 if (aShare) { 175 layers::SharedSurfacesChild::Share(this); 176 } 177 178 return true; 179 } 180 181 void SourceSurfaceSharedData::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 182 SizeOfInfo& aInfo) const { 183 MutexAutoLock lock(mMutex); 184 aInfo.AddType(SurfaceType::DATA_SHARED); 185 if (mBuf) { 186 aInfo.mNonHeapBytes = GetAlignedDataLength(); 187 } 188 if (!mClosed) { 189 aInfo.mExternalHandles = 1; 190 } 191 Maybe<wr::ExternalImageId> extId = SharedSurfacesChild::GetExternalId(this); 192 if (extId) { 193 aInfo.mExternalId = wr::AsUint64(extId.ref()); 194 } 195 } 196 197 uint8_t* SourceSurfaceSharedData::GetDataInternal() const { 198 mMutex.AssertCurrentThreadOwns(); 199 200 // This class's mappings are always mutable, so we can safely cast away the 201 // const in the values returned here (see the comment above the `mBuf` 202 // declaration). 203 204 // If we have an old buffer lingering, it is because we get reallocated to 205 // get a new handle to share, but there were still active mappings. 206 if (MOZ_UNLIKELY(mOldBuf)) { 207 MOZ_ASSERT(mMapCount > 0); 208 MOZ_ASSERT(mFinalized); 209 return const_cast<uint8_t*>(mOldBuf->DataAs<uint8_t>()); 210 } 211 // Const cast to match `GetData()`. 212 return const_cast<uint8_t*>(mBuf->DataAs<uint8_t>()); 213 } 214 215 nsresult SourceSurfaceSharedData::CloneHandle( 216 ipc::ReadOnlySharedMemoryHandle& aHandle) { 217 MutexAutoLock lock(mMutex); 218 MOZ_ASSERT(mHandleCount > 0); 219 220 if (mClosed) { 221 return NS_ERROR_NOT_AVAILABLE; 222 } 223 224 aHandle = mBufHandle.Clone().ToReadOnly(); 225 if (MOZ_UNLIKELY(!aHandle)) { 226 return NS_ERROR_FAILURE; 227 } 228 229 return NS_OK; 230 } 231 232 void SourceSurfaceSharedData::CloseHandleInternal() { 233 mMutex.AssertCurrentThreadOwns(); 234 235 if (mClosed) { 236 MOZ_ASSERT(mHandleCount == 0); 237 MOZ_ASSERT(mShared); 238 return; 239 } 240 241 if (mShared) { 242 mBufHandle = nullptr; 243 mClosed = true; 244 } 245 } 246 247 bool SourceSurfaceSharedData::ReallocHandle() { 248 MutexAutoLock lock(mMutex); 249 MOZ_ASSERT(mHandleCount > 0); 250 MOZ_ASSERT(mClosed); 251 252 if (NS_WARN_IF(!mFinalized)) { 253 // We haven't finished populating the surface data yet, which means we are 254 // out of luck, as we have no means of synchronizing with the producer to 255 // write new data to a new buffer. This should be fairly rare, caused by a 256 // crash in the GPU process, while we were decoding an image. 257 return false; 258 } 259 260 size_t len = GetAlignedDataLength(); 261 auto handle = ipc::shared_memory::Create(len); 262 auto mapping = handle.Map(); 263 if (NS_WARN_IF(!handle) || NS_WARN_IF(!mapping)) { 264 return false; 265 } 266 267 size_t copyLen = GetDataLength(); 268 memcpy(mapping.Address(), mBuf->Address(), copyLen); 269 #ifdef SHARED_SURFACE_PROTECT_FINALIZED 270 ipc::shared_memory::LocalProtect(mapping.DataAs<char>(), len, 271 ipc::shared_memory::AccessRead); 272 #endif 273 274 if (mMapCount > 0 && !mOldBuf) { 275 mOldBuf = std::move(mBuf); 276 } 277 mBufHandle = std::move(handle); 278 mBuf = std::make_shared<ipc::MutableOrReadOnlySharedMemoryMapping>( 279 std::move(mapping)); 280 mClosed = false; 281 mShared = false; 282 return true; 283 } 284 285 void SourceSurfaceSharedData::Finalize() { 286 MutexAutoLock lock(mMutex); 287 MOZ_ASSERT(!mFinalized); 288 289 #ifdef SHARED_SURFACE_PROTECT_FINALIZED 290 size_t len = GetAlignedDataLength(); 291 // This class's mappings are always mutable, so we can safely cast away the 292 // const (see the comment above the `mBuf` declaration). 293 ipc::shared_memory::LocalProtect(const_cast<char*>(mBuf->DataAs<char>()), len, 294 ipc::shared_memory::AccessRead); 295 #endif 296 297 mFinalized = true; 298 } 299 300 } // namespace gfx 301 } // namespace mozilla