/src/mozilla-central/gfx/layers/client/MultiTiledContentClient.cpp
Line | Count | Source (jump to first uncovered line) |
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 "mozilla/layers/MultiTiledContentClient.h" |
8 | | |
9 | | #include "ClientTiledPaintedLayer.h" |
10 | | #include "mozilla/layers/LayerMetricsWrapper.h" |
11 | | |
12 | | namespace mozilla { |
13 | | |
14 | | using namespace gfx; |
15 | | |
16 | | namespace layers { |
17 | | |
18 | | MultiTiledContentClient::MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer, |
19 | | ClientLayerManager* aManager) |
20 | | : TiledContentClient(aManager, "Multi") |
21 | | , mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper) |
22 | | , mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper) |
23 | 0 | { |
24 | 0 | MOZ_COUNT_CTOR(MultiTiledContentClient); |
25 | 0 | mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution()); |
26 | 0 | mHasLowPrecision = gfxPrefs::UseLowPrecisionBuffer(); |
27 | 0 | } |
28 | | |
29 | | void |
30 | | MultiTiledContentClient::ClearCachedResources() |
31 | 0 | { |
32 | 0 | CompositableClient::ClearCachedResources(); |
33 | 0 | mTiledBuffer.DiscardBuffers(); |
34 | 0 | mLowPrecisionTiledBuffer.DiscardBuffers(); |
35 | 0 | } |
36 | | |
37 | | void |
38 | | MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType) |
39 | 0 | { |
40 | 0 | ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER |
41 | 0 | ? &mLowPrecisionTiledBuffer |
42 | 0 | : &mTiledBuffer; |
43 | 0 |
|
44 | 0 | MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision); |
45 | 0 |
|
46 | 0 | mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles()); |
47 | 0 | } |
48 | | |
49 | | ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer, |
50 | | CompositableClient& aCompositableClient, |
51 | | ClientLayerManager* aManager, |
52 | | SharedFrameMetricsHelper* aHelper) |
53 | | : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient) |
54 | | , mManager(aManager) |
55 | | , mCallback(nullptr) |
56 | | , mCallbackData(nullptr) |
57 | | , mSharedFrameMetricsHelper(aHelper) |
58 | 0 | { |
59 | 0 | } |
60 | | |
61 | | void |
62 | | ClientMultiTiledLayerBuffer::DiscardBuffers() |
63 | 0 | { |
64 | 0 | for (TileClient& tile : mRetainedTiles) { |
65 | 0 | tile.DiscardBuffers(); |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | SurfaceDescriptorTiles |
70 | | ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles() |
71 | 0 | { |
72 | 0 | InfallibleTArray<TileDescriptor> tiles; |
73 | 0 |
|
74 | 0 | for (TileClient& tile : mRetainedTiles) { |
75 | 0 | TileDescriptor tileDesc = tile.GetTileDescriptor(); |
76 | 0 | tiles.AppendElement(tileDesc); |
77 | 0 | // Reset the update rect |
78 | 0 | tile.mUpdateRect = IntRect(); |
79 | 0 | } |
80 | 0 | return SurfaceDescriptorTiles(mValidRegion, |
81 | 0 | tiles, |
82 | 0 | mTileOrigin, mTileSize, |
83 | 0 | mTiles.mFirst.x, mTiles.mFirst.y, |
84 | 0 | mTiles.mSize.width, mTiles.mSize.height, |
85 | 0 | mResolution, mFrameResolution.xScale, |
86 | 0 | mFrameResolution.yScale, |
87 | 0 | mWasLastPaintProgressive); |
88 | 0 | } |
89 | | |
90 | | void |
91 | | ClientMultiTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, |
92 | | const nsIntRegion& aPaintRegion, |
93 | | const nsIntRegion& aDirtyRegion, |
94 | | LayerManager::DrawPaintedLayerCallback aCallback, |
95 | | void* aCallbackData, |
96 | | TilePaintFlags aFlags) |
97 | 0 | { |
98 | 0 | TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer, Stringify(aPaintRegion).c_str()); |
99 | 0 | TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer, Stringify(aNewValidRegion).c_str()); |
100 | 0 |
|
101 | 0 | mCallback = aCallback; |
102 | 0 | mCallbackData = aCallbackData; |
103 | 0 | mWasLastPaintProgressive = !!(aFlags & TilePaintFlags::Progressive); |
104 | 0 |
|
105 | | #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
106 | | long start = PR_IntervalNow(); |
107 | | #endif |
108 | |
|
109 | | #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
110 | | if (PR_IntervalNow() - start > 30) { |
111 | | const IntRect bounds = aPaintRegion.GetBounds(); |
112 | | printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); |
113 | | if (aPaintRegion.IsComplex()) { |
114 | | printf_stderr("Complex region\n"); |
115 | | for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) { |
116 | | const IntRect& rect = iter.Get(); |
117 | | printf_stderr(" rect %i, %i, %i, %i\n", |
118 | | rect.x, rect.y, rect.width, rect.height); |
119 | | } |
120 | | } |
121 | | } |
122 | | start = PR_IntervalNow(); |
123 | | #endif |
124 | |
|
125 | 0 | AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::PaintThebes", GRAPHICS); |
126 | 0 |
|
127 | 0 | mNewValidRegion = aNewValidRegion; |
128 | 0 | Update(aNewValidRegion, aPaintRegion, aDirtyRegion, aFlags); |
129 | 0 |
|
130 | | #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
131 | | if (PR_IntervalNow() - start > 10) { |
132 | | const IntRect bounds = aPaintRegion.GetBounds(); |
133 | | printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height); |
134 | | } |
135 | | #endif |
136 | |
|
137 | 0 | mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode); |
138 | 0 | mCallback = nullptr; |
139 | 0 | mCallbackData = nullptr; |
140 | 0 | } |
141 | | |
142 | | void ClientMultiTiledLayerBuffer::MaybeSyncTextures(const nsIntRegion& aPaintRegion, |
143 | | const TilesPlacement& aNewTiles, |
144 | | const IntSize& aScaledTileSize) |
145 | 0 | { |
146 | 0 | if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) { |
147 | 0 | AutoTArray<uint64_t, 10> syncTextureSerials; |
148 | 0 | SurfaceMode mode; |
149 | 0 | Unused << GetContentType(&mode); |
150 | 0 |
|
151 | 0 | // Pre-pass through the tiles (mirroring the filter logic below) to gather |
152 | 0 | // texture IDs that we need to ensure are unused by the GPU before we |
153 | 0 | // continue. |
154 | 0 | if (!aPaintRegion.IsEmpty()) { |
155 | 0 | MOZ_ASSERT(mPaintTasks.IsEmpty()); |
156 | 0 | for (size_t i = 0; i < mRetainedTiles.Length(); ++i) { |
157 | 0 | const TileCoordIntPoint tileCoord = aNewTiles.TileCoord(i); |
158 | 0 |
|
159 | 0 | IntPoint tileOffset = GetTileOffset(tileCoord); |
160 | 0 | nsIntRegion tileDrawRegion = IntRect(tileOffset, aScaledTileSize); |
161 | 0 | tileDrawRegion.AndWith(aPaintRegion); |
162 | 0 |
|
163 | 0 | if (tileDrawRegion.IsEmpty()) { |
164 | 0 | continue; |
165 | 0 | } |
166 | 0 | |
167 | 0 | TileClient& tile = mRetainedTiles[i]; |
168 | 0 | tile.GetSyncTextureSerials(mode, syncTextureSerials); |
169 | 0 | } |
170 | 0 | } |
171 | 0 |
|
172 | 0 | if (syncTextureSerials.Length() > 0) { |
173 | 0 | mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials); |
174 | 0 | } |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion, |
179 | | const nsIntRegion& aPaintRegion, |
180 | | const nsIntRegion& aDirtyRegion, |
181 | | TilePaintFlags aFlags) |
182 | 0 | { |
183 | 0 | const IntSize scaledTileSize = GetScaledTileSize(); |
184 | 0 | const gfx::IntRect newBounds = newValidRegion.GetBounds(); |
185 | 0 |
|
186 | 0 | const TilesPlacement oldTiles = mTiles; |
187 | 0 | const TilesPlacement newTiles(floor_div(newBounds.X(), scaledTileSize.width), |
188 | 0 | floor_div(newBounds.Y(), scaledTileSize.height), |
189 | 0 | floor_div(GetTileStart(newBounds.X(), scaledTileSize.width) |
190 | 0 | + newBounds.Width(), scaledTileSize.width) + 1, |
191 | 0 | floor_div(GetTileStart(newBounds.Y(), scaledTileSize.height) |
192 | 0 | + newBounds.Height(), scaledTileSize.height) + 1); |
193 | 0 |
|
194 | 0 | const size_t oldTileCount = mRetainedTiles.Length(); |
195 | 0 | const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height; |
196 | 0 |
|
197 | 0 | nsTArray<TileClient> oldRetainedTiles; |
198 | 0 | mRetainedTiles.SwapElements(oldRetainedTiles); |
199 | 0 | mRetainedTiles.SetLength(newTileCount); |
200 | 0 |
|
201 | 0 | for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) { |
202 | 0 | const TileCoordIntPoint tileCoord = oldTiles.TileCoord(oldIndex); |
203 | 0 | const size_t newIndex = newTiles.TileIndex(tileCoord); |
204 | 0 | // First, get the already existing tiles to the right place in the new array. |
205 | 0 | // Leave placeholders (default constructor) where there was no tile. |
206 | 0 | if (newTiles.HasTile(tileCoord)) { |
207 | 0 | mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex]; |
208 | 0 | } else { |
209 | 0 | // release tiles that we are not going to reuse before allocating new ones |
210 | 0 | // to avoid allocating unnecessarily. |
211 | 0 | oldRetainedTiles[oldIndex].DiscardBuffers(); |
212 | 0 | } |
213 | 0 | } |
214 | 0 |
|
215 | 0 | oldRetainedTiles.Clear(); |
216 | 0 |
|
217 | 0 | nsIntRegion paintRegion = aPaintRegion; |
218 | 0 | nsIntRegion dirtyRegion = aDirtyRegion; |
219 | 0 |
|
220 | 0 | MaybeSyncTextures(paintRegion, newTiles, scaledTileSize); |
221 | 0 |
|
222 | 0 | if (!paintRegion.IsEmpty()) { |
223 | 0 | MOZ_ASSERT(mPaintTasks.IsEmpty()); |
224 | 0 |
|
225 | 0 | for (size_t i = 0; i < newTileCount; ++i) { |
226 | 0 | const TileCoordIntPoint tileCoord = newTiles.TileCoord(i); |
227 | 0 |
|
228 | 0 | IntPoint tileOffset = GetTileOffset(tileCoord); |
229 | 0 | nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize); |
230 | 0 | tileDrawRegion.AndWith(paintRegion); |
231 | 0 |
|
232 | 0 | if (tileDrawRegion.IsEmpty()) { |
233 | 0 | continue; |
234 | 0 | } |
235 | 0 | |
236 | 0 | TileClient& tile = mRetainedTiles[i]; |
237 | 0 | if (!ValidateTile(tile, GetTileOffset(tileCoord), tileDrawRegion, aFlags)) { |
238 | 0 | gfxCriticalError() << "ValidateTile failed"; |
239 | 0 | } |
240 | 0 |
|
241 | 0 | // Validating the tile may have required more to be painted. |
242 | 0 | paintRegion.OrWith(tileDrawRegion); |
243 | 0 | dirtyRegion.OrWith(tileDrawRegion); |
244 | 0 | } |
245 | 0 |
|
246 | 0 | if (!mPaintTiles.IsEmpty()) { |
247 | 0 | // Create a tiled draw target |
248 | 0 | gfx::TileSet tileset; |
249 | 0 | for (size_t i = 0; i < mPaintTiles.Length(); ++i) { |
250 | 0 | mPaintTiles[i].mTileOrigin -= mTilingOrigin; |
251 | 0 | } |
252 | 0 | tileset.mTiles = mPaintTiles.Elements(); |
253 | 0 | tileset.mTileCount = mPaintTiles.Length(); |
254 | 0 | RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset); |
255 | 0 | if (!drawTarget || !drawTarget->IsValid()) { |
256 | 0 | gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target"; |
257 | 0 | return; |
258 | 0 | } |
259 | 0 | drawTarget->SetTransform(Matrix()); |
260 | 0 |
|
261 | 0 | // Draw into the tiled draw target |
262 | 0 | RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget); |
263 | 0 | MOZ_ASSERT(ctx); // already checked the draw target above |
264 | 0 | ctx->SetMatrix( |
265 | 0 | ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin)); |
266 | 0 |
|
267 | 0 | mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion, |
268 | 0 | DrawRegionClip::DRAW, nsIntRegion(), mCallbackData); |
269 | 0 | ctx = nullptr; |
270 | 0 |
|
271 | 0 | // Edge padding allows us to avoid resampling artifacts |
272 | 0 | if (gfxPrefs::TileEdgePaddingEnabled() && mResolution == 1) { |
273 | 0 | drawTarget->PadEdges(newValidRegion.MovedBy(-mTilingOrigin)); |
274 | 0 | } |
275 | 0 |
|
276 | 0 | // Reset |
277 | 0 | mPaintTiles.Clear(); |
278 | 0 | mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(), |
279 | 0 | std::numeric_limits<int32_t>::max()); |
280 | 0 | } |
281 | 0 |
|
282 | 0 | // Dispatch to the paint thread |
283 | 0 | if (aFlags & TilePaintFlags::Async) { |
284 | 0 | bool queuedTask = false; |
285 | 0 |
|
286 | 0 | while (!mPaintTasks.IsEmpty()) { |
287 | 0 | UniquePtr<PaintTask> task = mPaintTasks.PopLastElement(); |
288 | 0 | if (!task->mCapture->IsEmpty()) { |
289 | 0 | PaintThread::Get()->QueuePaintTask(std::move(task)); |
290 | 0 | queuedTask = true; |
291 | 0 | } |
292 | 0 | } |
293 | 0 |
|
294 | 0 | if (queuedTask) { |
295 | 0 | mManager->SetQueuedAsyncPaints(); |
296 | 0 | } |
297 | 0 |
|
298 | 0 | mPaintTasks.Clear(); |
299 | 0 | } |
300 | 0 |
|
301 | 0 | for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) { |
302 | 0 | TileClient& tile = mRetainedTiles[i]; |
303 | 0 | UnlockTile(tile); |
304 | 0 | } |
305 | 0 | } |
306 | 0 |
|
307 | 0 | mTiles = newTiles; |
308 | 0 | mValidRegion = newValidRegion; |
309 | 0 | } |
310 | | |
311 | | bool |
312 | | ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile, |
313 | | const nsIntPoint& aTileOrigin, |
314 | | nsIntRegion& aDirtyRegion, |
315 | | TilePaintFlags aFlags) |
316 | 0 | { |
317 | 0 | AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::ValidateTile", GRAPHICS); |
318 | 0 |
|
319 | | #ifdef GFX_TILEDLAYER_PREF_WARNINGS |
320 | | if (aDirtyRegion.IsComplex()) { |
321 | | printf_stderr("Complex region\n"); |
322 | | } |
323 | | #endif |
324 | |
|
325 | 0 | SurfaceMode mode; |
326 | 0 | gfxContentType content = GetContentType(&mode); |
327 | 0 |
|
328 | 0 | if (!aTile.mAllocator) { |
329 | 0 | aTile.SetTextureAllocator(mManager->GetCompositorBridgeChild()->GetTexturePool( |
330 | 0 | mManager->AsShadowForwarder(), |
331 | 0 | gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content), |
332 | 0 | TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD | TextureFlags::NON_BLOCKING_READ_LOCK)); |
333 | 0 | MOZ_ASSERT(aTile.mAllocator); |
334 | 0 | } |
335 | 0 |
|
336 | 0 | nsIntRegion tileDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin); |
337 | 0 | tileDirtyRegion.ScaleRoundOut(mResolution, mResolution); |
338 | 0 |
|
339 | 0 | nsIntRegion tileVisibleRegion = mNewValidRegion.MovedBy(-aTileOrigin); |
340 | 0 | tileVisibleRegion.ScaleRoundOut(mResolution, mResolution); |
341 | 0 |
|
342 | 0 | std::vector<RefPtr<TextureClient>> asyncPaintClients; |
343 | 0 |
|
344 | 0 | Maybe<AcquiredBackBuffer> backBuffer = |
345 | 0 | aTile.AcquireBackBuffer(mCompositableClient, |
346 | 0 | tileDirtyRegion, |
347 | 0 | tileVisibleRegion, |
348 | 0 | content, |
349 | 0 | mode, |
350 | 0 | aFlags); |
351 | 0 |
|
352 | 0 | if (!backBuffer) { |
353 | 0 | return false; |
354 | 0 | } |
355 | 0 | |
356 | 0 | // Mark the area we need to paint in the back buffer as invalid in the |
357 | 0 | // front buffer as they will become out of sync. |
358 | 0 | aTile.mInvalidFront.OrWith(tileDirtyRegion); |
359 | 0 |
|
360 | 0 | // Add the backbuffer's invalid region intersected with the visible region to the |
361 | 0 | // dirty region we will be painting. This will be empty if we are able to copy |
362 | 0 | // from the front into the back. |
363 | 0 | nsIntRegion tileInvalidRegion = aTile.mInvalidBack; |
364 | 0 | tileInvalidRegion.AndWith(tileVisibleRegion); |
365 | 0 |
|
366 | 0 | nsIntRegion invalidRegion = tileInvalidRegion; |
367 | 0 | invalidRegion.MoveBy(aTileOrigin); |
368 | 0 | invalidRegion.ScaleInverseRoundOut(mResolution, mResolution); |
369 | 0 |
|
370 | 0 | tileDirtyRegion.OrWith(tileInvalidRegion); |
371 | 0 | aDirtyRegion.OrWith(invalidRegion); |
372 | 0 |
|
373 | 0 | // Mark the region we will be painting and the region we copied from the front buffer as |
374 | 0 | // needing to be uploaded to the compositor |
375 | 0 | aTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(backBuffer->mUpdatedRect); |
376 | 0 |
|
377 | 0 | // We need to clear the dirty region of the tile before painting |
378 | 0 | // if we are painting non-opaque content |
379 | 0 | if (mode != SurfaceMode::SURFACE_OPAQUE) { |
380 | 0 | for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) { |
381 | 0 | const gfx::Rect drawRect(iter.Get().X(), iter.Get().Y(), |
382 | 0 | iter.Get().Width(), iter.Get().Height()); |
383 | 0 | backBuffer->mTarget->ClearRect(drawRect); |
384 | 0 | } |
385 | 0 | } |
386 | 0 |
|
387 | 0 | gfx::Tile paintTile; |
388 | 0 | paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y); |
389 | 0 | paintTile.mDrawTarget = backBuffer->mTarget; |
390 | 0 | mPaintTiles.AppendElement(paintTile); |
391 | 0 |
|
392 | 0 | if (aFlags & TilePaintFlags::Async) { |
393 | 0 | UniquePtr<PaintTask> task(new PaintTask()); |
394 | 0 | task->mCapture = backBuffer->mCapture; |
395 | 0 | task->mTarget = backBuffer->mBackBuffer; |
396 | 0 | task->mClients = std::move(backBuffer->mTextureClients); |
397 | 0 | mPaintTasks.AppendElement(std::move(task)); |
398 | 0 | } else { |
399 | 0 | MOZ_RELEASE_ASSERT(backBuffer->mTarget == backBuffer->mBackBuffer); |
400 | 0 | MOZ_RELEASE_ASSERT(backBuffer->mCapture == nullptr); |
401 | 0 | } |
402 | 0 |
|
403 | 0 | mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x); |
404 | 0 | mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y); |
405 | 0 |
|
406 | 0 | // The new buffer is now validated, remove the dirty region from it. |
407 | 0 | aTile.mInvalidBack.SubOut(tileDirtyRegion); |
408 | 0 |
|
409 | 0 | aTile.Flip(); |
410 | 0 |
|
411 | 0 | return true; |
412 | 0 | } |
413 | | |
414 | | /** |
415 | | * This function takes the transform stored in aTransformToCompBounds |
416 | | * (which was generated in GetTransformToAncestorsParentLayer), and |
417 | | * modifies it with the ViewTransform from the compositor side so that |
418 | | * it reflects what the compositor is actually rendering. This operation |
419 | | * basically adds in the layer's async transform. |
420 | | * This function then returns the scroll ancestor's composition bounds, |
421 | | * transformed into the painted layer's LayerPixel coordinates, accounting |
422 | | * for the compositor state. |
423 | | */ |
424 | | static Maybe<LayerRect> |
425 | | GetCompositorSideCompositionBounds(const LayerMetricsWrapper& aScrollAncestor, |
426 | | const LayerToParentLayerMatrix4x4& aTransformToCompBounds, |
427 | | const AsyncTransform& aAPZTransform, |
428 | | const LayerRect& aClip) |
429 | 0 | { |
430 | 0 | LayerToParentLayerMatrix4x4 transform = aTransformToCompBounds * |
431 | 0 | AsyncTransformComponentMatrix(aAPZTransform); |
432 | 0 |
|
433 | 0 | return UntransformBy(transform.Inverse(), |
434 | 0 | aScrollAncestor.Metrics().GetCompositionBounds(), aClip); |
435 | 0 | } |
436 | | |
437 | | bool |
438 | | ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion, |
439 | | const nsIntRegion& aOldValidRegion, |
440 | | nsIntRegion& aRegionToPaint, |
441 | | BasicTiledLayerPaintData* aPaintData, |
442 | | bool aIsRepeated) |
443 | 0 | { |
444 | 0 | aRegionToPaint = aInvalidRegion; |
445 | 0 |
|
446 | 0 | // If the composition bounds rect is empty, we can't make any sensible |
447 | 0 | // decision about how to update coherently. In this case, just update |
448 | 0 | // everything in one transaction. |
449 | 0 | if (aPaintData->mCompositionBounds.IsEmpty()) { |
450 | 0 | aPaintData->mPaintFinished = true; |
451 | 0 | return false; |
452 | 0 | } |
453 | 0 | |
454 | 0 | // If this is a low precision buffer, we force progressive updates. The |
455 | 0 | // assumption is that the contents is less important, so visual coherency |
456 | 0 | // is lower priority than speed. |
457 | 0 | bool drawingLowPrecision = IsLowPrecision(); |
458 | 0 |
|
459 | 0 | // Find out if we have any non-stale content to update. |
460 | 0 | nsIntRegion staleRegion; |
461 | 0 | staleRegion.And(aInvalidRegion, aOldValidRegion); |
462 | 0 |
|
463 | 0 | TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer, Stringify(staleRegion).c_str()); |
464 | 0 |
|
465 | 0 | LayerMetricsWrapper scrollAncestor; |
466 | 0 | mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr); |
467 | 0 |
|
468 | 0 | // Find out the current view transform to determine which tiles to draw |
469 | 0 | // first, and see if we should just abort this paint. Aborting is usually |
470 | 0 | // caused by there being an incoming, more relevant paint. |
471 | 0 | AsyncTransform viewTransform; |
472 | 0 | MOZ_ASSERT(mSharedFrameMetricsHelper); |
473 | 0 |
|
474 | 0 | bool abortPaint = |
475 | 0 | mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics( |
476 | 0 | scrollAncestor, |
477 | 0 | !staleRegion.Contains(aInvalidRegion), |
478 | 0 | drawingLowPrecision, |
479 | 0 | viewTransform); |
480 | 0 |
|
481 | 0 | TILING_LOG("TILING %p: Progressive update view transform %s zoom %f abort %d\n", |
482 | 0 | &mPaintedLayer, ToString(viewTransform.mTranslation).c_str(), viewTransform.mScale.scale, abortPaint); |
483 | 0 |
|
484 | 0 | if (abortPaint) { |
485 | 0 | // We ignore if front-end wants to abort if this is the first, |
486 | 0 | // non-low-precision paint, as in that situation, we're about to override |
487 | 0 | // front-end's page/viewport metrics. |
488 | 0 | if (!aPaintData->mFirstPaint || drawingLowPrecision) { |
489 | 0 | AUTO_PROFILER_LABEL( |
490 | 0 | "ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion", |
491 | 0 | GRAPHICS); |
492 | 0 |
|
493 | 0 | aRegionToPaint.SetEmpty(); |
494 | 0 | return aIsRepeated; |
495 | 0 | } |
496 | 0 | } |
497 | 0 |
|
498 | 0 | Maybe<LayerRect> transformedCompositionBounds = |
499 | 0 | GetCompositorSideCompositionBounds(scrollAncestor, |
500 | 0 | aPaintData->mTransformToCompBounds, |
501 | 0 | viewTransform, |
502 | 0 | LayerRect(mPaintedLayer.GetVisibleRegion().GetBounds())); |
503 | 0 |
|
504 | 0 | if (!transformedCompositionBounds) { |
505 | 0 | aPaintData->mPaintFinished = true; |
506 | 0 | return false; |
507 | 0 | } |
508 | 0 | |
509 | 0 | TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n", &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str()); |
510 | 0 |
|
511 | 0 | // Compute a "coherent update rect" that we should paint all at once in a |
512 | 0 | // single transaction. This is to avoid rendering glitches on animated |
513 | 0 | // page content, and when layers change size/shape. |
514 | 0 | // On Fennec uploads are more expensive because we're not using gralloc, so |
515 | 0 | // we use a coherent update rect that is intersected with the screen at the |
516 | 0 | // time of issuing the draw command. This will paint faster but also potentially |
517 | 0 | // make the progressive paint more visible to the user while scrolling. |
518 | 0 | IntRect coherentUpdateRect(RoundedOut( |
519 | | #ifdef MOZ_WIDGET_ANDROID |
520 | | transformedCompositionBounds->Intersect(aPaintData->mCompositionBounds) |
521 | | #else |
522 | | *transformedCompositionBounds |
523 | 0 | #endif |
524 | 0 | ).ToUnknownRect()); |
525 | 0 |
|
526 | 0 | TILING_LOG("TILING %p: Progressive update final coherency rect %s\n", &mPaintedLayer, Stringify(coherentUpdateRect).c_str()); |
527 | 0 |
|
528 | 0 | aRegionToPaint.And(aInvalidRegion, coherentUpdateRect); |
529 | 0 | aRegionToPaint.Or(aRegionToPaint, staleRegion); |
530 | 0 | bool drawingStale = !aRegionToPaint.IsEmpty(); |
531 | 0 | if (!drawingStale) { |
532 | 0 | aRegionToPaint = aInvalidRegion; |
533 | 0 | } |
534 | 0 |
|
535 | 0 | // Prioritise tiles that are currently visible on the screen. |
536 | 0 | bool paintingVisible = false; |
537 | 0 | if (aRegionToPaint.Intersects(coherentUpdateRect)) { |
538 | 0 | aRegionToPaint.And(aRegionToPaint, coherentUpdateRect); |
539 | 0 | paintingVisible = true; |
540 | 0 | } |
541 | 0 |
|
542 | 0 | TILING_LOG("TILING %p: Progressive update final paint region %s\n", &mPaintedLayer, Stringify(aRegionToPaint).c_str()); |
543 | 0 |
|
544 | 0 | // Paint area that's visible and overlaps previously valid content to avoid |
545 | 0 | // visible glitches in animated elements, such as gifs. |
546 | 0 | bool paintInSingleTransaction = paintingVisible && (drawingStale || aPaintData->mFirstPaint); |
547 | 0 |
|
548 | 0 | TILING_LOG("TILING %p: paintingVisible %d drawingStale %d firstPaint %d singleTransaction %d\n", |
549 | 0 | &mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint, paintInSingleTransaction); |
550 | 0 |
|
551 | 0 | // The following code decides what order to draw tiles in, based on the |
552 | 0 | // current scroll direction of the primary scrollable layer. |
553 | 0 | NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!"); |
554 | 0 | IntRect paintBounds = aRegionToPaint.GetBounds(); |
555 | 0 |
|
556 | 0 | int startX, incX, startY, incY; |
557 | 0 | gfx::IntSize scaledTileSize = GetScaledTileSize(); |
558 | 0 | if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) { |
559 | 0 | startX = RoundDownToTileEdge(paintBounds.X(), scaledTileSize.width); |
560 | 0 | incX = scaledTileSize.width; |
561 | 0 | } else { |
562 | 0 | startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width); |
563 | 0 | incX = -scaledTileSize.width; |
564 | 0 | } |
565 | 0 |
|
566 | 0 | if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) { |
567 | 0 | startY = RoundDownToTileEdge(paintBounds.Y(), scaledTileSize.height); |
568 | 0 | incY = scaledTileSize.height; |
569 | 0 | } else { |
570 | 0 | startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height); |
571 | 0 | incY = -scaledTileSize.height; |
572 | 0 | } |
573 | 0 |
|
574 | 0 | // Find a tile to draw. |
575 | 0 | IntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height); |
576 | 0 | int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x; |
577 | 0 | int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y; |
578 | 0 | // This loop will always terminate, as there is at least one tile area |
579 | 0 | // along the first/last row/column intersecting with regionToPaint, or its |
580 | 0 | // bounds would have been smaller. |
581 | 0 | while (true) { |
582 | 0 | aRegionToPaint.And(aInvalidRegion, tileBounds); |
583 | 0 | if (!aRegionToPaint.IsEmpty()) { |
584 | 0 | if (mResolution != 1) { |
585 | 0 | // Paint the entire tile for low-res. This is aimed to fixing low-res resampling |
586 | 0 | // and to avoid doing costly region accurate painting for a small area. |
587 | 0 | aRegionToPaint = tileBounds; |
588 | 0 | } |
589 | 0 | break; |
590 | 0 | } |
591 | 0 | if (Abs(scrollDiffY) >= Abs(scrollDiffX)) { |
592 | 0 | tileBounds.MoveByX(incX); |
593 | 0 | } else { |
594 | 0 | tileBounds.MoveByY(incY); |
595 | 0 | } |
596 | 0 | } |
597 | 0 |
|
598 | 0 | if (!aRegionToPaint.Contains(aInvalidRegion)) { |
599 | 0 | // The region needed to paint is larger then our progressive chunk size |
600 | 0 | // therefore update what we want to paint and ask for a new paint transaction. |
601 | 0 |
|
602 | 0 | // If we need to draw more than one tile to maintain coherency, make |
603 | 0 | // sure it happens in the same transaction by requesting this work be |
604 | 0 | // repeated immediately. |
605 | 0 | // If this is unnecessary, the remaining work will be done tile-by-tile in |
606 | 0 | // subsequent transactions. The caller code is responsible for scheduling |
607 | 0 | // the subsequent transactions as long as we don't set the mPaintFinished |
608 | 0 | // flag to true. |
609 | 0 | return (!drawingLowPrecision && paintInSingleTransaction); |
610 | 0 | } |
611 | 0 |
|
612 | 0 | // We're not repeating painting and we've not requested a repeat transaction, |
613 | 0 | // so the paint is finished. If there's still a separate low precision |
614 | 0 | // paint to do, it will get marked as unfinished later. |
615 | 0 | aPaintData->mPaintFinished = true; |
616 | 0 | return false; |
617 | 0 | } |
618 | | |
619 | | bool |
620 | | ClientMultiTiledLayerBuffer::ProgressiveUpdate(const nsIntRegion& aValidRegion, |
621 | | const nsIntRegion& aInvalidRegion, |
622 | | const nsIntRegion& aOldValidRegion, |
623 | | nsIntRegion& aOutDrawnRegion, |
624 | | BasicTiledLayerPaintData* aPaintData, |
625 | | LayerManager::DrawPaintedLayerCallback aCallback, |
626 | | void* aCallbackData) |
627 | 0 | { |
628 | 0 | TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer, Stringify(aValidRegion).c_str()); |
629 | 0 | TILING_LOG("TILING %p: Progressive update invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str()); |
630 | 0 | TILING_LOG("TILING %p: Progressive update old valid region %s\n", &mPaintedLayer, Stringify(aOldValidRegion).c_str()); |
631 | 0 |
|
632 | 0 | bool repeat = false; |
633 | 0 | bool isBufferChanged = false; |
634 | 0 | nsIntRegion remainingInvalidRegion = aInvalidRegion; |
635 | 0 | nsIntRegion updatedValidRegion = aValidRegion; |
636 | 0 | do { |
637 | 0 | // Compute the region that should be updated. Repeat as many times as |
638 | 0 | // is required. |
639 | 0 | nsIntRegion regionToPaint; |
640 | 0 | repeat = ComputeProgressiveUpdateRegion(remainingInvalidRegion, |
641 | 0 | aOldValidRegion, |
642 | 0 | regionToPaint, |
643 | 0 | aPaintData, |
644 | 0 | repeat); |
645 | 0 |
|
646 | 0 | TILING_LOG("TILING %p: Progressive update computed paint region %s repeat %d\n", &mPaintedLayer, Stringify(regionToPaint).c_str(), repeat); |
647 | 0 |
|
648 | 0 | // There's no further work to be done. |
649 | 0 | if (regionToPaint.IsEmpty()) { |
650 | 0 | break; |
651 | 0 | } |
652 | 0 | |
653 | 0 | isBufferChanged = true; |
654 | 0 |
|
655 | 0 | // Keep track of what we're about to refresh. |
656 | 0 | aOutDrawnRegion.OrWith(regionToPaint); |
657 | 0 | updatedValidRegion.OrWith(regionToPaint); |
658 | 0 |
|
659 | 0 | // aValidRegion may have been altered by InvalidateRegion, but we still |
660 | 0 | // want to display stale content until it gets progressively updated. |
661 | 0 | // Create a region that includes stale content. |
662 | 0 | nsIntRegion validOrStale; |
663 | 0 | validOrStale.Or(updatedValidRegion, aOldValidRegion); |
664 | 0 |
|
665 | 0 | // Paint the computed region and subtract it from the invalid region. |
666 | 0 | PaintThebes(validOrStale, regionToPaint, remainingInvalidRegion, |
667 | 0 | aCallback, aCallbackData, TilePaintFlags::Progressive); |
668 | 0 | remainingInvalidRegion.SubOut(regionToPaint); |
669 | 0 | } while (repeat); |
670 | 0 |
|
671 | 0 | TILING_LOG("TILING %p: Progressive update final valid region %s buffer changed %d\n", &mPaintedLayer, Stringify(updatedValidRegion).c_str(), isBufferChanged); |
672 | 0 | TILING_LOG("TILING %p: Progressive update final invalid region %s\n", &mPaintedLayer, Stringify(remainingInvalidRegion).c_str()); |
673 | 0 |
|
674 | 0 | // Return false if nothing has been drawn, or give what has been drawn |
675 | 0 | // to the shadow layer to upload. |
676 | 0 | return isBufferChanged; |
677 | 0 | } |
678 | | |
679 | | } // namespace layers |
680 | | } // namespace mozilla |