Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / gfx / layers / SourceSurfaceSharedData.cpp
blob622f68855d00ea4b1f77b38d0afa9ea0c5f935d1
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/. */
7 #include "SourceSurfaceSharedData.h"
9 #include "mozilla/Likely.h"
10 #include "mozilla/StaticPrefs_image.h"
11 #include "mozilla/Types.h" // for decltype
12 #include "mozilla/layers/SharedSurfacesChild.h"
13 #include "mozilla/layers/SharedSurfacesParent.h"
14 #include "nsDebug.h" // for NS_ABORT_OOM
16 #include "base/process_util.h"
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.
25 # define SHARED_SURFACE_PROTECT_FINALIZED
26 #endif
28 using namespace mozilla::layers;
30 namespace mozilla {
31 namespace gfx {
33 void SourceSurfaceSharedDataWrapper::Init(const IntSize& aSize, int32_t aStride,
34 SurfaceFormat aFormat,
35 SharedMemoryBasic::Handle aHandle,
36 base::ProcessId aCreatorPid) {
37 MOZ_ASSERT(!mBuf);
38 mSize = aSize;
39 mStride = aStride;
40 mFormat = aFormat;
41 mCreatorPid = aCreatorPid;
43 size_t len = GetAlignedDataLength();
44 mBuf = MakeAndAddRef<SharedMemoryBasic>();
45 if (!mBuf->SetHandle(std::move(aHandle), ipc::SharedMemory::RightsReadOnly)) {
46 MOZ_CRASH("Invalid shared memory handle!");
49 bool mapped = EnsureMapped(len);
50 if ((sizeof(uintptr_t) <= 4 ||
51 StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) &&
52 len / 1024 >
53 StaticPrefs::image_mem_shared_unmap_min_threshold_kb_AtStartup()) {
54 mHandleLock.emplace("SourceSurfaceSharedDataWrapper::mHandleLock");
56 if (mapped) {
57 // Tracking at the initial mapping, and not just after the first use of
58 // the surface means we might get unmapped again before the next frame
59 // gets rendered if a low virtual memory condition persists.
60 SharedSurfacesParent::AddTracking(this);
62 } else if (!mapped) {
63 // We don't support unmapping for this surface, and we failed to map it.
64 NS_ABORT_OOM(len);
65 } else {
66 mBuf->CloseHandle();
70 void SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface) {
71 MOZ_ASSERT(!mBuf);
72 MOZ_ASSERT(aSurface);
73 mSize = aSurface->mSize;
74 mStride = aSurface->mStride;
75 mFormat = aSurface->mFormat;
76 mCreatorPid = base::GetCurrentProcId();
77 mBuf = aSurface->mBuf;
80 bool SourceSurfaceSharedDataWrapper::EnsureMapped(size_t aLength) {
81 MOZ_ASSERT(!GetData());
83 while (!mBuf->Map(aLength)) {
84 nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired;
85 if (!SharedSurfacesParent::AgeOneGeneration(expired)) {
86 return false;
88 MOZ_ASSERT(!expired.Contains(this));
89 SharedSurfacesParent::ExpireMap(expired);
92 return true;
95 bool SourceSurfaceSharedDataWrapper::Map(MapType aMapType,
96 MappedSurface* aMappedSurface) {
97 uint8_t* dataPtr;
99 if (aMapType != MapType::READ) {
100 // The data may be write-protected
101 return false;
104 if (mHandleLock) {
105 MutexAutoLock lock(*mHandleLock);
106 dataPtr = GetData();
107 if (mMapCount == 0) {
108 SharedSurfacesParent::RemoveTracking(this);
109 if (!dataPtr) {
110 size_t len = GetAlignedDataLength();
111 if (!EnsureMapped(len)) {
112 NS_ABORT_OOM(len);
114 dataPtr = GetData();
117 ++mMapCount;
118 } else {
119 dataPtr = GetData();
120 ++mMapCount;
123 MOZ_ASSERT(dataPtr);
124 aMappedSurface->mData = dataPtr;
125 aMappedSurface->mStride = mStride;
126 return true;
129 void SourceSurfaceSharedDataWrapper::Unmap() {
130 if (mHandleLock) {
131 MutexAutoLock lock(*mHandleLock);
132 if (--mMapCount == 0) {
133 SharedSurfacesParent::AddTracking(this);
135 } else {
136 --mMapCount;
138 MOZ_ASSERT(mMapCount >= 0);
141 void SourceSurfaceSharedDataWrapper::ExpireMap() {
142 MutexAutoLock lock(*mHandleLock);
143 if (mMapCount == 0) {
144 mBuf->Unmap();
148 bool SourceSurfaceSharedData::Init(const IntSize& aSize, int32_t aStride,
149 SurfaceFormat aFormat,
150 bool aShare /* = true */) {
151 mSize = aSize;
152 mStride = aStride;
153 mFormat = aFormat;
155 size_t len = GetAlignedDataLength();
156 mBuf = new SharedMemoryBasic();
157 if (NS_WARN_IF(!mBuf->Create(len)) || NS_WARN_IF(!mBuf->Map(len))) {
158 mBuf = nullptr;
159 return false;
162 if (aShare) {
163 layers::SharedSurfacesChild::Share(this);
166 return true;
169 void SourceSurfaceSharedData::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
170 SizeOfInfo& aInfo) const {
171 MutexAutoLock lock(mMutex);
172 aInfo.AddType(SurfaceType::DATA_SHARED);
173 if (mBuf) {
174 aInfo.mNonHeapBytes = GetAlignedDataLength();
176 if (!mClosed) {
177 aInfo.mExternalHandles = 1;
179 Maybe<wr::ExternalImageId> extId = SharedSurfacesChild::GetExternalId(this);
180 if (extId) {
181 aInfo.mExternalId = wr::AsUint64(extId.ref());
185 uint8_t* SourceSurfaceSharedData::GetDataInternal() const {
186 mMutex.AssertCurrentThreadOwns();
188 // If we have an old buffer lingering, it is because we get reallocated to
189 // get a new handle to share, but there were still active mappings.
190 if (MOZ_UNLIKELY(mOldBuf)) {
191 MOZ_ASSERT(mMapCount > 0);
192 MOZ_ASSERT(mFinalized);
193 return static_cast<uint8_t*>(mOldBuf->memory());
195 return static_cast<uint8_t*>(mBuf->memory());
198 nsresult SourceSurfaceSharedData::CloneHandle(
199 SharedMemoryBasic::Handle& aHandle) {
200 MutexAutoLock lock(mMutex);
201 MOZ_ASSERT(mHandleCount > 0);
203 if (mClosed) {
204 return NS_ERROR_NOT_AVAILABLE;
207 aHandle = mBuf->CloneHandle();
208 if (MOZ_UNLIKELY(!aHandle)) {
209 return NS_ERROR_FAILURE;
212 return NS_OK;
215 void SourceSurfaceSharedData::CloseHandleInternal() {
216 mMutex.AssertCurrentThreadOwns();
218 if (mClosed) {
219 MOZ_ASSERT(mHandleCount == 0);
220 MOZ_ASSERT(mShared);
221 return;
224 if (mShared) {
225 mBuf->CloseHandle();
226 mClosed = true;
230 bool SourceSurfaceSharedData::ReallocHandle() {
231 MutexAutoLock lock(mMutex);
232 MOZ_ASSERT(mHandleCount > 0);
233 MOZ_ASSERT(mClosed);
235 if (NS_WARN_IF(!mFinalized)) {
236 // We haven't finished populating the surface data yet, which means we are
237 // out of luck, as we have no means of synchronizing with the producer to
238 // write new data to a new buffer. This should be fairly rare, caused by a
239 // crash in the GPU process, while we were decoding an image.
240 return false;
243 size_t len = GetAlignedDataLength();
244 RefPtr<SharedMemoryBasic> buf = new SharedMemoryBasic();
245 if (NS_WARN_IF(!buf->Create(len)) || NS_WARN_IF(!buf->Map(len))) {
246 return false;
249 size_t copyLen = GetDataLength();
250 memcpy(buf->memory(), mBuf->memory(), copyLen);
251 #ifdef SHARED_SURFACE_PROTECT_FINALIZED
252 buf->Protect(static_cast<char*>(buf->memory()), len, RightsRead);
253 #endif
255 if (mMapCount > 0 && !mOldBuf) {
256 mOldBuf = std::move(mBuf);
258 mBuf = std::move(buf);
259 mClosed = false;
260 mShared = false;
261 return true;
264 void SourceSurfaceSharedData::Finalize() {
265 MutexAutoLock lock(mMutex);
266 MOZ_ASSERT(!mFinalized);
268 #ifdef SHARED_SURFACE_PROTECT_FINALIZED
269 size_t len = GetAlignedDataLength();
270 mBuf->Protect(static_cast<char*>(mBuf->memory()), len, RightsRead);
271 #endif
273 mFinalized = true;
276 } // namespace gfx
277 } // namespace mozilla