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 "CanvasChild.h"
9 #include "MainThreadUtils.h"
10 #include "mozilla/gfx/DrawTargetRecording.h"
11 #include "mozilla/gfx/Tools.h"
12 #include "mozilla/gfx/Rect.h"
13 #include "mozilla/gfx/Point.h"
14 #include "mozilla/ipc/Endpoint.h"
15 #include "mozilla/ipc/ProcessChild.h"
16 #include "mozilla/layers/CanvasDrawEventRecorder.h"
17 #include "nsIObserverService.h"
18 #include "RecordedCanvasEventImpl.h"
23 class RingBufferWriterServices final
24 : public CanvasEventRingBuffer::WriterServices
{
26 explicit RingBufferWriterServices(RefPtr
<CanvasChild
> aCanvasChild
)
27 : mCanvasChild(std::move(aCanvasChild
)) {}
29 ~RingBufferWriterServices() final
= default;
31 bool ReaderClosed() final
{
32 return !mCanvasChild
->GetIPCChannel()->CanSend() ||
33 ipc::ProcessChild::ExpectingShutdown();
36 void ResumeReader() final
{ mCanvasChild
->ResumeTranslation(); }
39 RefPtr
<CanvasChild
> mCanvasChild
;
42 class SourceSurfaceCanvasRecording final
: public gfx::SourceSurface
{
44 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording
, final
)
46 SourceSurfaceCanvasRecording(
47 const RefPtr
<gfx::SourceSurface
>& aRecordedSuface
,
48 CanvasChild
* aCanvasChild
,
49 const RefPtr
<CanvasDrawEventRecorder
>& aRecorder
)
50 : mRecordedSurface(aRecordedSuface
),
51 mCanvasChild(aCanvasChild
),
52 mRecorder(aRecorder
) {
53 mRecorder
->RecordEvent(RecordedAddSurfaceAlias(this, aRecordedSuface
));
54 mRecorder
->AddStoredObject(this);
57 ~SourceSurfaceCanvasRecording() {
58 ReleaseOnMainThread(std::move(mRecorder
), this, std::move(mRecordedSurface
),
59 std::move(mCanvasChild
));
62 gfx::SurfaceType
GetType() const final
{ return mRecordedSurface
->GetType(); }
64 gfx::IntSize
GetSize() const final
{ return mRecordedSurface
->GetSize(); }
66 gfx::SurfaceFormat
GetFormat() const final
{
67 return mRecordedSurface
->GetFormat();
70 already_AddRefed
<gfx::DataSourceSurface
> GetDataSurface() final
{
71 EnsureDataSurfaceOnMainThread();
72 return do_AddRef(mDataSourceSurface
);
76 void GuaranteePersistance() final
{ EnsureDataSurfaceOnMainThread(); }
79 void EnsureDataSurfaceOnMainThread() {
80 // The data can only be retrieved on the main thread.
81 if (!mDataSourceSurface
&& NS_IsMainThread()) {
82 mDataSourceSurface
= mCanvasChild
->GetDataSurface(mRecordedSurface
);
86 // Used to ensure that clean-up that requires it is done on the main thread.
87 static void ReleaseOnMainThread(RefPtr
<CanvasDrawEventRecorder
> aRecorder
,
88 ReferencePtr aSurfaceAlias
,
89 RefPtr
<gfx::SourceSurface
> aAliasedSurface
,
90 RefPtr
<CanvasChild
> aCanvasChild
) {
91 if (!NS_IsMainThread()) {
92 NS_DispatchToMainThread(NewRunnableFunction(
93 "SourceSurfaceCanvasRecording::ReleaseOnMainThread",
94 SourceSurfaceCanvasRecording::ReleaseOnMainThread
,
95 std::move(aRecorder
), aSurfaceAlias
, std::move(aAliasedSurface
),
96 std::move(aCanvasChild
)));
100 aRecorder
->RemoveStoredObject(aSurfaceAlias
);
101 aRecorder
->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias
));
102 aAliasedSurface
= nullptr;
103 aCanvasChild
= nullptr;
107 RefPtr
<gfx::SourceSurface
> mRecordedSurface
;
108 RefPtr
<CanvasChild
> mCanvasChild
;
109 RefPtr
<CanvasDrawEventRecorder
> mRecorder
;
110 RefPtr
<gfx::DataSourceSurface
> mDataSourceSurface
;
113 CanvasChild::CanvasChild(Endpoint
<PCanvasChild
>&& aEndpoint
) {
114 aEndpoint
.Bind(this);
117 CanvasChild::~CanvasChild() = default;
119 static void NotifyCanvasDeviceReset() {
120 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
122 obs
->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
126 ipc::IPCResult
CanvasChild::RecvNotifyDeviceChanged() {
127 NotifyCanvasDeviceReset();
128 mRecorder
->RecordEvent(RecordedDeviceChangeAcknowledged());
132 /* static */ bool CanvasChild::mDeactivated
= false;
134 ipc::IPCResult
CanvasChild::RecvDeactivate() {
136 NotifyCanvasDeviceReset();
140 void CanvasChild::EnsureRecorder(TextureType aTextureType
) {
142 MOZ_ASSERT(mTextureType
== TextureType::Unknown
);
143 mTextureType
= aTextureType
;
144 mRecorder
= MakeAndAddRef
<CanvasDrawEventRecorder
>();
145 SharedMemoryBasic::Handle handle
;
146 CrossProcessSemaphoreHandle readerSem
;
147 CrossProcessSemaphoreHandle writerSem
;
148 if (!mRecorder
->Init(OtherPid(), &handle
, &readerSem
, &writerSem
,
149 MakeUnique
<RingBufferWriterServices
>(this))) {
155 Unused
<< SendInitTranslator(mTextureType
, handle
, readerSem
, writerSem
);
159 MOZ_RELEASE_ASSERT(mTextureType
== aTextureType
,
160 "We only support one remote TextureType currently.");
163 void CanvasChild::ActorDestroy(ActorDestroyReason aWhy
) {
164 // Explicitly drop our reference to the recorder, because it holds a reference
165 // to us via the ResumeTranslation callback.
169 void CanvasChild::ResumeTranslation() {
171 SendResumeTranslation();
175 void CanvasChild::Destroy() {
181 void CanvasChild::OnTextureWriteLock() {
182 // We drop mRecorder in ActorDestroy to break the reference cycle.
187 mHasOutstandingWriteLock
= true;
188 mLastWriteLockCheckpoint
= mRecorder
->CreateCheckpoint();
191 void CanvasChild::OnTextureForwarded() {
192 // We drop mRecorder in ActorDestroy to break the reference cycle.
197 if (mHasOutstandingWriteLock
) {
198 mRecorder
->RecordEvent(RecordedCanvasFlush());
199 if (!mRecorder
->WaitForCheckpoint(mLastWriteLockCheckpoint
)) {
200 gfxWarning() << "Timed out waiting for last write lock to be processed.";
203 mHasOutstandingWriteLock
= false;
207 void CanvasChild::EnsureBeginTransaction() {
208 // We drop mRecorder in ActorDestroy to break the reference cycle.
213 if (!mIsInTransaction
) {
214 mRecorder
->RecordEvent(RecordedCanvasBeginTransaction());
215 mIsInTransaction
= true;
219 void CanvasChild::EndTransaction() {
220 // We drop mRecorder in ActorDestroy to break the reference cycle.
225 if (mIsInTransaction
) {
226 mRecorder
->RecordEvent(RecordedCanvasEndTransaction());
227 mIsInTransaction
= false;
228 mLastNonEmptyTransaction
= TimeStamp::NowLoRes();
231 ++mTransactionsSinceGetDataSurface
;
234 bool CanvasChild::ShouldBeCleanedUp() const {
235 // Always return true if we've been deactivated.
240 // We can only be cleaned up if nothing else references our recorder.
241 if (mRecorder
&& !mRecorder
->hasOneRef()) {
245 static const TimeDuration kCleanUpCanvasThreshold
=
246 TimeDuration::FromSeconds(10);
247 return TimeStamp::NowLoRes() - mLastNonEmptyTransaction
>
248 kCleanUpCanvasThreshold
;
251 already_AddRefed
<gfx::DrawTarget
> CanvasChild::CreateDrawTarget(
252 gfx::IntSize aSize
, gfx::SurfaceFormat aFormat
) {
253 // We drop mRecorder in ActorDestroy to break the reference cycle.
258 RefPtr
<gfx::DrawTarget
> dummyDt
= gfx::Factory::CreateDrawTarget(
259 gfx::BackendType::SKIA
, gfx::IntSize(1, 1), aFormat
);
260 RefPtr
<gfx::DrawTarget
> dt
= MakeAndAddRef
<gfx::DrawTargetRecording
>(
261 mRecorder
, dummyDt
, gfx::IntRect(gfx::IntPoint(0, 0), aSize
));
265 void CanvasChild::RecordEvent(const gfx::RecordedEvent
& aEvent
) {
266 // We drop mRecorder in ActorDestroy to break the reference cycle.
271 mRecorder
->RecordEvent(aEvent
);
274 already_AddRefed
<gfx::DataSourceSurface
> CanvasChild::GetDataSurface(
275 const gfx::SourceSurface
* aSurface
) {
276 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
277 MOZ_ASSERT(aSurface
);
279 // We drop mRecorder in ActorDestroy to break the reference cycle.
284 mTransactionsSinceGetDataSurface
= 0;
285 EnsureBeginTransaction();
286 mRecorder
->RecordEvent(RecordedPrepareDataForSurface(aSurface
));
287 uint32_t checkpoint
= mRecorder
->CreateCheckpoint();
289 gfx::IntSize ssSize
= aSurface
->GetSize();
290 gfx::SurfaceFormat ssFormat
= aSurface
->GetFormat();
291 size_t dataFormatWidth
= ssSize
.width
* BytesPerPixel(ssFormat
);
292 RefPtr
<gfx::DataSourceSurface
> dataSurface
=
293 gfx::Factory::CreateDataSourceSurfaceWithStride(ssSize
, ssFormat
,
296 gfxWarning() << "Failed to create DataSourceSurface.";
299 gfx::DataSourceSurface::ScopedMap
map(dataSurface
,
300 gfx::DataSourceSurface::READ_WRITE
);
301 char* dest
= reinterpret_cast<char*>(map
.GetData());
302 if (!mRecorder
->WaitForCheckpoint(checkpoint
)) {
303 gfxWarning() << "Timed out preparing data for DataSourceSurface.";
304 return dataSurface
.forget();
307 mRecorder
->RecordEvent(RecordedGetDataForSurface(aSurface
));
308 mRecorder
->ReturnRead(dest
, ssSize
.height
* dataFormatWidth
);
310 return dataSurface
.forget();
313 already_AddRefed
<gfx::SourceSurface
> CanvasChild::WrapSurface(
314 const RefPtr
<gfx::SourceSurface
>& aSurface
) {
315 MOZ_ASSERT(aSurface
);
316 // We drop mRecorder in ActorDestroy to break the reference cycle.
321 return MakeAndAddRef
<SourceSurfaceCanvasRecording
>(aSurface
, this, mRecorder
);
324 } // namespace layers
325 } // namespace mozilla