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 "CanvasChild.h"
8 
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"
19 
20 namespace mozilla {
21 namespace layers {
22 
23 class RingBufferWriterServices final
24     : public CanvasEventRingBuffer::WriterServices {
25  public:
RingBufferWriterServices(RefPtr<CanvasChild> aCanvasChild)26   explicit RingBufferWriterServices(RefPtr<CanvasChild> aCanvasChild)
27       : mCanvasChild(std::move(aCanvasChild)) {}
28 
29   ~RingBufferWriterServices() final = default;
30 
ReaderClosed()31   bool ReaderClosed() final {
32     return !mCanvasChild->GetIPCChannel()->CanSend() ||
33            ipc::ProcessChild::ExpectingShutdown();
34   }
35 
ResumeReader()36   void ResumeReader() final { mCanvasChild->ResumeTranslation(); }
37 
38  private:
39   RefPtr<CanvasChild> mCanvasChild;
40 };
41 
42 class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
43  public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording,final)44   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final)
45 
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);
55   }
56 
~SourceSurfaceCanvasRecording()57   ~SourceSurfaceCanvasRecording() {
58     ReleaseOnMainThread(std::move(mRecorder), this, std::move(mRecordedSurface),
59                         std::move(mCanvasChild));
60   }
61 
GetType() const62   gfx::SurfaceType GetType() const final { return mRecordedSurface->GetType(); }
63 
GetSize() const64   gfx::IntSize GetSize() const final { return mRecordedSurface->GetSize(); }
65 
GetFormat() const66   gfx::SurfaceFormat GetFormat() const final {
67     return mRecordedSurface->GetFormat();
68   }
69 
GetDataSurface()70   already_AddRefed<gfx::DataSourceSurface> GetDataSurface() final {
71     EnsureDataSurfaceOnMainThread();
72     return do_AddRef(mDataSourceSurface);
73   }
74 
75  private:
EnsureDataSurfaceOnMainThread()76   void EnsureDataSurfaceOnMainThread() {
77     // The data can only be retrieved on the main thread.
78     if (!mDataSourceSurface && NS_IsMainThread()) {
79       mDataSourceSurface = mCanvasChild->GetDataSurface(mRecordedSurface);
80     }
81   }
82 
83   // Used to ensure that clean-up that requires it is done on the main thread.
ReleaseOnMainThread(RefPtr<CanvasDrawEventRecorder> aRecorder,ReferencePtr aSurfaceAlias,RefPtr<gfx::SourceSurface> aAliasedSurface,RefPtr<CanvasChild> aCanvasChild)84   static void ReleaseOnMainThread(RefPtr<CanvasDrawEventRecorder> aRecorder,
85                                   ReferencePtr aSurfaceAlias,
86                                   RefPtr<gfx::SourceSurface> aAliasedSurface,
87                                   RefPtr<CanvasChild> aCanvasChild) {
88     if (!NS_IsMainThread()) {
89       NS_DispatchToMainThread(NewRunnableFunction(
90           "SourceSurfaceCanvasRecording::ReleaseOnMainThread",
91           SourceSurfaceCanvasRecording::ReleaseOnMainThread,
92           std::move(aRecorder), aSurfaceAlias, std::move(aAliasedSurface),
93           std::move(aCanvasChild)));
94       return;
95     }
96 
97     aRecorder->RemoveStoredObject(aSurfaceAlias);
98     aRecorder->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias));
99     aAliasedSurface = nullptr;
100     aCanvasChild = nullptr;
101     aRecorder = nullptr;
102   }
103 
104   RefPtr<gfx::SourceSurface> mRecordedSurface;
105   RefPtr<CanvasChild> mCanvasChild;
106   RefPtr<CanvasDrawEventRecorder> mRecorder;
107   RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
108 };
109 
CanvasChild(Endpoint<PCanvasChild> && aEndpoint)110 CanvasChild::CanvasChild(Endpoint<PCanvasChild>&& aEndpoint) {
111   aEndpoint.Bind(this);
112 }
113 
114 CanvasChild::~CanvasChild() = default;
115 
NotifyCanvasDeviceReset()116 static void NotifyCanvasDeviceReset() {
117   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
118   if (obs) {
119     obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
120   }
121 }
122 
RecvNotifyDeviceChanged()123 ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() {
124   NotifyCanvasDeviceReset();
125   mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged());
126   return IPC_OK();
127 }
128 
129 /* static */ bool CanvasChild::mDeactivated = false;
130 
RecvDeactivate()131 ipc::IPCResult CanvasChild::RecvDeactivate() {
132   mDeactivated = true;
133   NotifyCanvasDeviceReset();
134   return IPC_OK();
135 }
136 
EnsureRecorder(TextureType aTextureType)137 void CanvasChild::EnsureRecorder(TextureType aTextureType) {
138   if (!mRecorder) {
139     MOZ_ASSERT(mTextureType == TextureType::Unknown);
140     mTextureType = aTextureType;
141     mRecorder = MakeAndAddRef<CanvasDrawEventRecorder>();
142     SharedMemoryBasic::Handle handle;
143     CrossProcessSemaphoreHandle readerSem;
144     CrossProcessSemaphoreHandle writerSem;
145     if (!mRecorder->Init(OtherPid(), &handle, &readerSem, &writerSem,
146                          MakeUnique<RingBufferWriterServices>(this))) {
147       mRecorder = nullptr;
148       return;
149     }
150 
151     if (CanSend()) {
152       Unused << SendInitTranslator(mTextureType, std::move(handle),
153                                    std::move(readerSem), std::move(writerSem));
154     }
155   }
156 
157   MOZ_RELEASE_ASSERT(mTextureType == aTextureType,
158                      "We only support one remote TextureType currently.");
159 }
160 
ActorDestroy(ActorDestroyReason aWhy)161 void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {
162   // Explicitly drop our reference to the recorder, because it holds a reference
163   // to us via the ResumeTranslation callback.
164   mRecorder = nullptr;
165 }
166 
ResumeTranslation()167 void CanvasChild::ResumeTranslation() {
168   if (CanSend()) {
169     SendResumeTranslation();
170   }
171 }
172 
Destroy()173 void CanvasChild::Destroy() {
174   if (CanSend()) {
175     Close();
176   }
177 }
178 
OnTextureWriteLock()179 void CanvasChild::OnTextureWriteLock() {
180   // We drop mRecorder in ActorDestroy to break the reference cycle.
181   if (!mRecorder) {
182     return;
183   }
184 
185   mHasOutstandingWriteLock = true;
186   mLastWriteLockCheckpoint = mRecorder->CreateCheckpoint();
187 }
188 
OnTextureForwarded()189 void CanvasChild::OnTextureForwarded() {
190   // We drop mRecorder in ActorDestroy to break the reference cycle.
191   if (!mRecorder) {
192     return;
193   }
194 
195   if (mHasOutstandingWriteLock) {
196     mRecorder->RecordEvent(RecordedCanvasFlush());
197     if (!mRecorder->WaitForCheckpoint(mLastWriteLockCheckpoint)) {
198       gfxWarning() << "Timed out waiting for last write lock to be processed.";
199     }
200 
201     mHasOutstandingWriteLock = false;
202   }
203 
204   // We hold onto the last transaction's external surfaces until we have waited
205   // for the write locks in this transaction. This means we know that the
206   // surfaces have been picked up in the canvas threads and there is no race
207   // with them being removed from SharedSurfacesParent. Note this releases the
208   // current contents of mLastTransactionExternalSurfaces.
209   mRecorder->TakeExternalSurfaces(mLastTransactionExternalSurfaces);
210 }
211 
EnsureBeginTransaction()212 void CanvasChild::EnsureBeginTransaction() {
213   // We drop mRecorder in ActorDestroy to break the reference cycle.
214   if (!mRecorder) {
215     return;
216   }
217 
218   if (!mIsInTransaction) {
219     mRecorder->RecordEvent(RecordedCanvasBeginTransaction());
220     mIsInTransaction = true;
221   }
222 }
223 
EndTransaction()224 void CanvasChild::EndTransaction() {
225   // We drop mRecorder in ActorDestroy to break the reference cycle.
226   if (!mRecorder) {
227     return;
228   }
229 
230   if (mIsInTransaction) {
231     mRecorder->RecordEvent(RecordedCanvasEndTransaction());
232     mIsInTransaction = false;
233     mLastNonEmptyTransaction = TimeStamp::NowLoRes();
234   }
235 
236   ++mTransactionsSinceGetDataSurface;
237 }
238 
ShouldBeCleanedUp() const239 bool CanvasChild::ShouldBeCleanedUp() const {
240   // Always return true if we've been deactivated.
241   if (Deactivated()) {
242     return true;
243   }
244 
245   // We can only be cleaned up if nothing else references our recorder.
246   if (mRecorder && !mRecorder->hasOneRef()) {
247     return false;
248   }
249 
250   static const TimeDuration kCleanUpCanvasThreshold =
251       TimeDuration::FromSeconds(10);
252   return TimeStamp::NowLoRes() - mLastNonEmptyTransaction >
253          kCleanUpCanvasThreshold;
254 }
255 
CreateDrawTarget(gfx::IntSize aSize,gfx::SurfaceFormat aFormat)256 already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget(
257     gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
258   // We drop mRecorder in ActorDestroy to break the reference cycle.
259   if (!mRecorder) {
260     return nullptr;
261   }
262 
263   RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
264       gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
265   RefPtr<gfx::DrawTarget> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
266       mRecorder, dummyDt, gfx::IntRect(gfx::IntPoint(0, 0), aSize));
267   return dt.forget();
268 }
269 
RecordEvent(const gfx::RecordedEvent & aEvent)270 void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
271   // We drop mRecorder in ActorDestroy to break the reference cycle.
272   if (!mRecorder) {
273     return;
274   }
275 
276   mRecorder->RecordEvent(aEvent);
277 }
278 
GetDataSurface(const gfx::SourceSurface * aSurface)279 already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
280     const gfx::SourceSurface* aSurface) {
281   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
282   MOZ_ASSERT(aSurface);
283 
284   // We drop mRecorder in ActorDestroy to break the reference cycle.
285   if (!mRecorder) {
286     return nullptr;
287   }
288 
289   mTransactionsSinceGetDataSurface = 0;
290   EnsureBeginTransaction();
291   mRecorder->RecordEvent(RecordedPrepareDataForSurface(aSurface));
292   uint32_t checkpoint = mRecorder->CreateCheckpoint();
293 
294   gfx::IntSize ssSize = aSurface->GetSize();
295   gfx::SurfaceFormat ssFormat = aSurface->GetFormat();
296   size_t dataFormatWidth = ssSize.width * BytesPerPixel(ssFormat);
297   RefPtr<gfx::DataSourceSurface> dataSurface =
298       gfx::Factory::CreateDataSourceSurfaceWithStride(ssSize, ssFormat,
299                                                       dataFormatWidth);
300   if (!dataSurface) {
301     gfxWarning() << "Failed to create DataSourceSurface.";
302     return nullptr;
303   }
304   gfx::DataSourceSurface::ScopedMap map(dataSurface,
305                                         gfx::DataSourceSurface::READ_WRITE);
306   char* dest = reinterpret_cast<char*>(map.GetData());
307   if (!mRecorder->WaitForCheckpoint(checkpoint)) {
308     gfxWarning() << "Timed out preparing data for DataSourceSurface.";
309     return dataSurface.forget();
310   }
311 
312   mRecorder->RecordEvent(RecordedGetDataForSurface(aSurface));
313   mRecorder->ReturnRead(dest, ssSize.height * dataFormatWidth);
314 
315   return dataSurface.forget();
316 }
317 
WrapSurface(const RefPtr<gfx::SourceSurface> & aSurface)318 already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
319     const RefPtr<gfx::SourceSurface>& aSurface) {
320   MOZ_ASSERT(aSurface);
321   // We drop mRecorder in ActorDestroy to break the reference cycle.
322   if (!mRecorder) {
323     return nullptr;
324   }
325 
326   return MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder);
327 }
328 
329 }  // namespace layers
330 }  // namespace mozilla
331