Source code

Revision control

Other Tools

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