Bug 1690340 - Part 2: Use the new naming for the developer tools menu items. r=jdescottes
[gecko.git] / gfx / layers / ipc / CanvasChild.cpp
blob144d383d89f2e9e27916d52634f78c3e33c0d706
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"
20 namespace mozilla {
21 namespace layers {
23 class RingBufferWriterServices final
24 : public CanvasEventRingBuffer::WriterServices {
25 public:
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(); }
38 private:
39 RefPtr<CanvasChild> mCanvasChild;
42 class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
43 public:
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);
75 protected:
76 void GuaranteePersistance() final { EnsureDataSurfaceOnMainThread(); }
78 private:
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)));
97 return;
100 aRecorder->RemoveStoredObject(aSurfaceAlias);
101 aRecorder->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias));
102 aAliasedSurface = nullptr;
103 aCanvasChild = nullptr;
104 aRecorder = 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();
121 if (obs) {
122 obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
126 ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() {
127 NotifyCanvasDeviceReset();
128 mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged());
129 return IPC_OK();
132 /* static */ bool CanvasChild::mDeactivated = false;
134 ipc::IPCResult CanvasChild::RecvDeactivate() {
135 mDeactivated = true;
136 NotifyCanvasDeviceReset();
137 return IPC_OK();
140 void CanvasChild::EnsureRecorder(TextureType aTextureType) {
141 if (!mRecorder) {
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))) {
150 mRecorder = nullptr;
151 return;
154 if (CanSend()) {
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.
166 mRecorder = nullptr;
169 void CanvasChild::ResumeTranslation() {
170 if (CanSend()) {
171 SendResumeTranslation();
175 void CanvasChild::Destroy() {
176 if (CanSend()) {
177 Close();
181 void CanvasChild::OnTextureWriteLock() {
182 // We drop mRecorder in ActorDestroy to break the reference cycle.
183 if (!mRecorder) {
184 return;
187 mHasOutstandingWriteLock = true;
188 mLastWriteLockCheckpoint = mRecorder->CreateCheckpoint();
191 void CanvasChild::OnTextureForwarded() {
192 // We drop mRecorder in ActorDestroy to break the reference cycle.
193 if (!mRecorder) {
194 return;
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.
209 if (!mRecorder) {
210 return;
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.
221 if (!mRecorder) {
222 return;
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.
236 if (Deactivated()) {
237 return true;
240 // We can only be cleaned up if nothing else references our recorder.
241 if (mRecorder && !mRecorder->hasOneRef()) {
242 return false;
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.
254 if (!mRecorder) {
255 return nullptr;
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));
262 return dt.forget();
265 void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
266 // We drop mRecorder in ActorDestroy to break the reference cycle.
267 if (!mRecorder) {
268 return;
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.
280 if (!mRecorder) {
281 return nullptr;
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,
294 dataFormatWidth);
295 if (!dataSurface) {
296 gfxWarning() << "Failed to create DataSourceSurface.";
297 return nullptr;
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.
317 if (!mRecorder) {
318 return nullptr;
321 return MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder);
324 } // namespace layers
325 } // namespace mozilla