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"
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
28 using namespace mozilla::layers
;
33 void SourceSurfaceSharedDataWrapper::Init(const IntSize
& aSize
, int32_t aStride
,
34 SurfaceFormat aFormat
,
35 SharedMemoryBasic::Handle aHandle
,
36 base::ProcessId aCreatorPid
) {
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()) &&
53 StaticPrefs::image_mem_shared_unmap_min_threshold_kb_AtStartup()) {
54 mHandleLock
.emplace("SourceSurfaceSharedDataWrapper::mHandleLock");
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);
63 // We don't support unmapping for this surface, and we failed to map it.
70 void SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData
* 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
)) {
88 MOZ_ASSERT(!expired
.Contains(this));
89 SharedSurfacesParent::ExpireMap(expired
);
95 bool SourceSurfaceSharedDataWrapper::Map(MapType aMapType
,
96 MappedSurface
* aMappedSurface
) {
99 if (aMapType
!= MapType::READ
) {
100 // The data may be write-protected
105 MutexAutoLock
lock(*mHandleLock
);
107 if (mMapCount
== 0) {
108 SharedSurfacesParent::RemoveTracking(this);
110 size_t len
= GetAlignedDataLength();
111 if (!EnsureMapped(len
)) {
124 aMappedSurface
->mData
= dataPtr
;
125 aMappedSurface
->mStride
= mStride
;
129 void SourceSurfaceSharedDataWrapper::Unmap() {
131 MutexAutoLock
lock(*mHandleLock
);
132 if (--mMapCount
== 0) {
133 SharedSurfacesParent::AddTracking(this);
138 MOZ_ASSERT(mMapCount
>= 0);
141 void SourceSurfaceSharedDataWrapper::ExpireMap() {
142 MutexAutoLock
lock(*mHandleLock
);
143 if (mMapCount
== 0) {
148 bool SourceSurfaceSharedData::Init(const IntSize
& aSize
, int32_t aStride
,
149 SurfaceFormat aFormat
,
150 bool aShare
/* = true */) {
155 size_t len
= GetAlignedDataLength();
156 mBuf
= new SharedMemoryBasic();
157 if (NS_WARN_IF(!mBuf
->Create(len
)) || NS_WARN_IF(!mBuf
->Map(len
))) {
163 layers::SharedSurfacesChild::Share(this);
169 void SourceSurfaceSharedData::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
170 SizeOfInfo
& aInfo
) const {
171 MutexAutoLock
lock(mMutex
);
172 aInfo
.AddType(SurfaceType::DATA_SHARED
);
174 aInfo
.mNonHeapBytes
= GetAlignedDataLength();
177 aInfo
.mExternalHandles
= 1;
179 Maybe
<wr::ExternalImageId
> extId
= SharedSurfacesChild::GetExternalId(this);
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);
204 return NS_ERROR_NOT_AVAILABLE
;
207 aHandle
= mBuf
->CloneHandle();
208 if (MOZ_UNLIKELY(!aHandle
)) {
209 return NS_ERROR_FAILURE
;
215 void SourceSurfaceSharedData::CloseHandleInternal() {
216 mMutex
.AssertCurrentThreadOwns();
219 MOZ_ASSERT(mHandleCount
== 0);
230 bool SourceSurfaceSharedData::ReallocHandle() {
231 MutexAutoLock
lock(mMutex
);
232 MOZ_ASSERT(mHandleCount
> 0);
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.
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
))) {
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
);
255 if (mMapCount
> 0 && !mOldBuf
) {
256 mOldBuf
= std::move(mBuf
);
258 mBuf
= std::move(buf
);
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
);
277 } // namespace mozilla