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 "ClientPaintedLayer.h"
8 #include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
9 #include <stdint.h> // for uint32_t
10 #include "GeckoProfiler.h" // for AUTO_PROFILER_LABEL
11 #include "client/ClientLayerManager.h" // for ClientLayerManager, etc
12 #include "gfxContext.h" // for gfxContext
13 #include "gfx2DGlue.h"
14 #include "gfxRect.h" // for gfxRect
15 #include "gfxPrefs.h" // for gfxPrefs
16 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
17 #include "mozilla/gfx/2D.h" // for DrawTarget
18 #include "mozilla/gfx/DrawEventRecorder.h"
19 #include "mozilla/gfx/Matrix.h" // for Matrix
20 #include "mozilla/gfx/Rect.h" // for Rect, IntRect
21 #include "mozilla/gfx/Types.h" // for Float, etc
22 #include "mozilla/layers/LayersTypes.h"
23 #include "mozilla/Preferences.h"
24 #include "nsCOMPtr.h" // for already_AddRefed
25 #include "nsISupportsImpl.h" // for Layer::AddRef, etc
26 #include "nsRect.h" // for mozilla::gfx::IntRect
27 #include "PaintThread.h"
28 #include "ReadbackProcessor.h"
29 #include "RotatedBuffer.h"
30
31 namespace mozilla {
32 namespace layers {
33
34 using namespace mozilla::gfx;
35
EnsureContentClient()36 bool ClientPaintedLayer::EnsureContentClient() {
37 if (!mContentClient) {
38 mContentClient = ContentClient::CreateContentClient(
39 ClientManager()->AsShadowForwarder());
40
41 if (!mContentClient) {
42 return false;
43 }
44
45 mContentClient->Connect();
46 ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
47 MOZ_ASSERT(mContentClient->GetForwarder());
48 }
49
50 return true;
51 }
52
CanRecordLayer(ReadbackProcessor * aReadback)53 bool ClientPaintedLayer::CanRecordLayer(ReadbackProcessor* aReadback) {
54 // If we don't have a paint thread, this is either not the content
55 // process or the pref is disabled.
56 if (!PaintThread::Get()) {
57 return false;
58 }
59
60 // Not supported yet
61 if (aReadback && UsedForReadback()) {
62 return false;
63 }
64
65 return true;
66 }
67
UpdateContentClient(PaintState & aState)68 void ClientPaintedLayer::UpdateContentClient(PaintState& aState) {
69 Mutated();
70
71 AddToValidRegion(aState.mRegionToDraw);
72
73 ContentClientRemoteBuffer* contentClientRemote =
74 static_cast<ContentClientRemoteBuffer*>(mContentClient.get());
75 MOZ_ASSERT(contentClientRemote->GetIPCHandle());
76
77 // Hold(this) ensures this layer is kept alive through the current transaction
78 // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
79 // so deleting this Hold for whatever reason will break things.
80 ClientManager()->Hold(this);
81 contentClientRemote->Updated(aState.mRegionToDraw,
82 mVisibleRegion.ToUnknownRegion());
83 }
84
UpdatePaintRegion(PaintState & aState)85 bool ClientPaintedLayer::UpdatePaintRegion(PaintState& aState) {
86 SubtractFromValidRegion(aState.mRegionToInvalidate);
87
88 if (!aState.mRegionToDraw.IsEmpty() &&
89 !ClientManager()->GetPaintedLayerCallback()) {
90 ClientManager()->SetTransactionIncomplete();
91 mContentClient->EndPaint(nullptr);
92 return false;
93 }
94
95 // The area that became invalid and is visible needs to be repainted
96 // (this could be the whole visible area if our buffer switched
97 // from RGB to RGBA, because we might need to repaint with
98 // subpixel AA)
99 aState.mRegionToInvalidate.And(aState.mRegionToInvalidate,
100 GetLocalVisibleRegion().ToUnknownRegion());
101 return true;
102 }
103
GetPaintFlags()104 uint32_t ClientPaintedLayer::GetPaintFlags() {
105 uint32_t flags = ContentClient::PAINT_CAN_DRAW_ROTATED;
106 #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
107 if (ClientManager()->CompositorMightResample()) {
108 flags |= ContentClient::PAINT_WILL_RESAMPLE;
109 }
110 if (!(flags & ContentClient::PAINT_WILL_RESAMPLE)) {
111 if (MayResample()) {
112 flags |= ContentClient::PAINT_WILL_RESAMPLE;
113 }
114 }
115 #endif
116 return flags;
117 }
118
PaintThebes(nsTArray<ReadbackProcessor::Update> * aReadbackUpdates)119 void ClientPaintedLayer::PaintThebes(
120 nsTArray<ReadbackProcessor::Update>* aReadbackUpdates) {
121 AUTO_PROFILER_LABEL("ClientPaintedLayer::PaintThebes", GRAPHICS);
122
123 NS_ASSERTION(ClientManager()->InDrawing(), "Can only draw in drawing phase");
124
125 uint32_t flags = GetPaintFlags();
126
127 PaintState state = mContentClient->BeginPaint(this, flags);
128 if (!UpdatePaintRegion(state)) {
129 return;
130 }
131
132 bool didUpdate = false;
133 RotatedBuffer::DrawIterator iter;
134 while (DrawTarget* target =
135 mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
136 if (!target || !target->IsValid()) {
137 if (target) {
138 mContentClient->ReturnDrawTarget(target);
139 }
140 continue;
141 }
142
143 SetAntialiasingFlags(this, target);
144
145 RefPtr<gfxContext> ctx =
146 gfxContext::CreatePreservingTransformOrNull(target);
147 MOZ_ASSERT(ctx); // already checked the target above
148
149 ClientManager()->GetPaintedLayerCallback()(
150 this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip,
151 state.mRegionToInvalidate,
152 ClientManager()->GetPaintedLayerCallbackData());
153
154 ctx = nullptr;
155 mContentClient->ReturnDrawTarget(target);
156 didUpdate = true;
157 }
158
159 mContentClient->EndPaint(aReadbackUpdates);
160
161 if (didUpdate) {
162 UpdateContentClient(state);
163 }
164 }
165
166 class MOZ_RAII AutoQueuedAsyncPaint {
167 public:
AutoQueuedAsyncPaint(ClientLayerManager * aLayerManager)168 explicit AutoQueuedAsyncPaint(ClientLayerManager* aLayerManager)
169 : mLayerManager(aLayerManager), mQueuedAsyncPaints(false) {}
170
Queue()171 void Queue() { mQueuedAsyncPaints = true; }
172
~AutoQueuedAsyncPaint()173 ~AutoQueuedAsyncPaint() {
174 if (mQueuedAsyncPaints) {
175 mLayerManager->SetQueuedAsyncPaints();
176 }
177 }
178
179 private:
180 ClientLayerManager* mLayerManager;
181 bool mQueuedAsyncPaints;
182 };
183
184 /***
185 * If we can, let's paint this ClientPaintedLayer's contents off the main
186 * thread. The essential idea is that we ask the ContentClient for a DrawTarget
187 * and record the moz2d commands. On the Paint Thread, we replay those commands
188 * to the destination draw target. There are a couple of lifetime issues here
189 * though:
190 *
191 * 1) TextureClient owns the underlying buffer and DrawTarget. Because of this
192 * we have to keep the TextureClient and DrawTarget alive but trick the
193 * TextureClient into thinking it's already returned the DrawTarget
194 * since we iterate through different Rects to get DrawTargets*. If
195 * the TextureClient goes away, the DrawTarget and thus buffer can too.
196 * 2) When ContentClient::EndPaint happens, it flushes the DrawTarget. We have
197 * to Reflush on the Paint Thread
198 * 3) DrawTarget API is NOT thread safe. We get around this by recording
199 * on the main thread and painting on the paint thread. Logically,
200 * ClientLayerManager will force a flushed paint and block the main thread
201 * if we have another transaction. Thus we have a gap between when the main
202 * thread records, the paint thread paints, and we block the main thread
203 * from trying to paint again. The underlying API however is NOT thread safe.
204 * 4) We have both "sync" and "async" OMTP. Sync OMTP means we paint on the
205 * main thread but block the main thread while the paint thread paints. Async
206 * OMTP doesn't block the main thread. Sync OMTP is only meant to be used as
207 * a debugging tool.
208 */
PaintOffMainThread()209 void ClientPaintedLayer::PaintOffMainThread() {
210 AutoQueuedAsyncPaint asyncPaints(ClientManager());
211
212 uint32_t flags = GetPaintFlags();
213 PaintState state =
214 mContentClient->BeginPaint(this, flags | ContentClient::PAINT_ASYNC);
215
216 if (state.mBufferState && state.mBufferState->HasOperations()) {
217 PaintThread::Get()->PrepareBuffer(state.mBufferState);
218 asyncPaints.Queue();
219 }
220
221 if (!UpdatePaintRegion(state)) {
222 return;
223 }
224
225 bool didUpdate = false;
226 RotatedBuffer::DrawIterator iter;
227
228 // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP.
229 while (RefPtr<CapturedPaintState> captureState =
230 mContentClient->BorrowDrawTargetForRecording(state, &iter)) {
231 DrawTarget* target = captureState->mTargetDual;
232 if (!target || !target->IsValid()) {
233 if (target) {
234 mContentClient->ReturnDrawTarget(target);
235 }
236 continue;
237 }
238
239 RefPtr<DrawTargetCapture> captureDT = Factory::CreateCaptureDrawTarget(
240 target->GetBackendType(), target->GetSize(), target->GetFormat());
241
242 captureDT->SetTransform(captureState->mTargetTransform);
243 SetAntialiasingFlags(this, captureDT);
244
245 RefPtr<gfxContext> ctx =
246 gfxContext::CreatePreservingTransformOrNull(captureDT);
247 MOZ_ASSERT(ctx); // already checked the target above
248
249 ClientManager()->GetPaintedLayerCallback()(
250 this, ctx, iter.mDrawRegion, iter.mDrawRegion, state.mClip,
251 state.mRegionToInvalidate,
252 ClientManager()->GetPaintedLayerCallbackData());
253
254 ctx = nullptr;
255
256 captureState->mCapture = captureDT.forget();
257 PaintThread::Get()->PaintContents(
258 captureState, ContentClient::PrepareDrawTargetForPainting);
259
260 mContentClient->ReturnDrawTarget(target);
261
262 asyncPaints.Queue();
263 didUpdate = true;
264 }
265
266 PaintThread::Get()->EndLayer();
267 mContentClient->EndPaint(nullptr);
268
269 if (didUpdate) {
270 UpdateContentClient(state);
271 }
272 }
273
RenderLayerWithReadback(ReadbackProcessor * aReadback)274 void ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor* aReadback) {
275 RenderMaskLayers(this);
276
277 if (!EnsureContentClient()) {
278 return;
279 }
280
281 if (CanRecordLayer(aReadback)) {
282 PaintOffMainThread();
283 return;
284 }
285
286 nsTArray<ReadbackProcessor::Update> readbackUpdates;
287 nsIntRegion readbackRegion;
288 if (aReadback && UsedForReadback()) {
289 aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
290 }
291
292 PaintThebes(&readbackUpdates);
293 }
294
CreatePaintedLayer()295 already_AddRefed<PaintedLayer> ClientLayerManager::CreatePaintedLayer() {
296 return CreatePaintedLayerWithHint(NONE);
297 }
298
CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)299 already_AddRefed<PaintedLayer> ClientLayerManager::CreatePaintedLayerWithHint(
300 PaintedLayerCreationHint aHint) {
301 NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
302 if (gfxPlatform::GetPlatform()->UsesTiling()) {
303 RefPtr<ClientTiledPaintedLayer> layer =
304 new ClientTiledPaintedLayer(this, aHint);
305 CREATE_SHADOW(Painted);
306 return layer.forget();
307 } else {
308 RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
309 CREATE_SHADOW(Painted);
310 return layer.forget();
311 }
312 }
313
PrintInfo(std::stringstream & aStream,const char * aPrefix)314 void ClientPaintedLayer::PrintInfo(std::stringstream& aStream,
315 const char* aPrefix) {
316 PaintedLayer::PrintInfo(aStream, aPrefix);
317 if (mContentClient) {
318 aStream << "\n";
319 nsAutoCString pfx(aPrefix);
320 pfx += " ";
321 mContentClient->PrintInfo(aStream, pfx.get());
322 }
323 }
324
325 } // namespace layers
326 } // namespace mozilla
327