Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "TiledContentHost.h"
7 #include "ThebesLayerComposite.h" // for ThebesLayerComposite
8 #include "mozilla/gfx/BaseSize.h" // for BaseSize
9 #include "mozilla/gfx/Matrix.h" // for Matrix4x4
10 #include "mozilla/layers/Compositor.h" // for Compositor
11 #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc
12 #include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
13 #include "nsAString.h"
14 #include "nsDebug.h" // for NS_WARNING
15 #include "nsPoint.h" // for nsIntPoint
16 #include "nsPrintfCString.h" // for nsPrintfCString
17 #include "nsRect.h" // for nsIntRect
18 #include "nsSize.h" // for nsIntSize
19 #include "mozilla/layers/TiledContentClient.h"
21 class gfxReusableSurfaceWrapper;
23 namespace mozilla {
24 using namespace gfx;
25 namespace layers {
27 class Layer;
29 TiledLayerBufferComposite::TiledLayerBufferComposite()
30 : mFrameResolution(1.0)
31 , mHasDoubleBufferedTiles(false)
32 , mUninitialized(true)
33 {}
35 /* static */ void
36 TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure)
37 {
38 textureHost->CompositorRecycle();
39 }
41 TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
42 const SurfaceDescriptorTiles& aDescriptor,
43 const nsIntRegion& aOldPaintedRegion)
44 {
45 mUninitialized = false;
46 mHasDoubleBufferedTiles = false;
47 mValidRegion = aDescriptor.validRegion();
48 mPaintedRegion = aDescriptor.paintedRegion();
49 mRetainedWidth = aDescriptor.retainedWidth();
50 mRetainedHeight = aDescriptor.retainedHeight();
51 mResolution = aDescriptor.resolution();
52 mFrameResolution = CSSToParentLayerScale(aDescriptor.frameResolution());
54 // Combine any valid content that wasn't already uploaded
55 nsIntRegion oldPaintedRegion(aOldPaintedRegion);
56 oldPaintedRegion.And(oldPaintedRegion, mValidRegion);
57 mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion);
59 const InfallibleTArray<TileDescriptor>& tiles = aDescriptor.tiles();
60 for(size_t i = 0; i < tiles.Length(); i++) {
61 RefPtr<TextureHost> texture;
62 const TileDescriptor& tileDesc = tiles[i];
63 switch (tileDesc.type()) {
64 case TileDescriptor::TTexturedTileDescriptor : {
65 texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
66 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
67 if (!gfxPrefs::LayersUseSimpleTiles()) {
68 texture->SetRecycleCallback(RecycleCallback, nullptr);
69 }
70 #endif
71 const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
72 nsRefPtr<gfxSharedReadLock> sharedLock;
73 if (ipcLock.type() == TileLock::TShmemSection) {
74 sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
75 } else {
76 sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
77 if (sharedLock) {
78 // The corresponding AddRef is in TiledClient::GetTileDescriptor
79 sharedLock->Release();
80 }
81 }
83 mRetainedTiles.AppendElement(TileHost(sharedLock, texture));
84 break;
85 }
86 default:
87 NS_WARNING("Unrecognised tile descriptor type");
88 // Fall through
89 case TileDescriptor::TPlaceholderTileDescriptor :
90 mRetainedTiles.AppendElement(GetPlaceholderTile());
91 break;
92 }
93 if (texture && !texture->HasInternalBuffer()) {
94 mHasDoubleBufferedTiles = true;
95 }
96 }
97 }
99 void
100 TiledLayerBufferComposite::ReadUnlock()
101 {
102 if (!IsValid()) {
103 return;
104 }
105 for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
106 mRetainedTiles[i].ReadUnlock();
107 }
108 }
110 void
111 TiledLayerBufferComposite::ReleaseTextureHosts()
112 {
113 if (!IsValid()) {
114 return;
115 }
116 for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
117 mRetainedTiles[i].mTextureHost = nullptr;
118 }
119 }
121 void
122 TiledLayerBufferComposite::Upload()
123 {
124 if(!IsValid()) {
125 return;
126 }
127 // The TextureClients were created with the TEXTURE_IMMEDIATE_UPLOAD flag,
128 // so calling Update on all the texture hosts will perform the texture upload.
129 Update(mValidRegion, mPaintedRegion);
130 ClearPaintedRegion();
131 }
133 TileHost
134 TiledLayerBufferComposite::ValidateTile(TileHost aTile,
135 const nsIntPoint& aTileOrigin,
136 const nsIntRegion& aDirtyRect)
137 {
138 if (aTile.IsPlaceholderTile()) {
139 NS_WARNING("Placeholder tile encountered in painted region");
140 return aTile;
141 }
143 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
144 printf_stderr("Upload tile %i, %i\n", aTileOrigin.x, aTileOrigin.y);
145 long start = PR_IntervalNow();
146 #endif
148 MOZ_ASSERT(aTile.mTextureHost->GetFlags() & TEXTURE_IMMEDIATE_UPLOAD);
149 // We possibly upload the entire texture contents here. This is a purposeful
150 // decision, as sub-image upload can often be slow and/or unreliable, but
151 // we may want to reevaluate this in the future.
152 // For !HasInternalBuffer() textures, this is likely a no-op.
153 aTile.mTextureHost->Updated(nullptr);
155 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
156 if (PR_IntervalNow() - start > 1) {
157 printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start);
158 }
159 #endif
160 return aTile;
161 }
163 void
164 TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
165 {
166 if (!IsValid()) {
167 return;
168 }
169 for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
170 if (mRetainedTiles[i].IsPlaceholderTile()) continue;
171 mRetainedTiles[i].mTextureHost->SetCompositor(aCompositor);
172 }
173 }
175 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
176 void
177 TiledLayerBufferComposite::SetReleaseFence(const android::sp<android::Fence>& aReleaseFence)
178 {
179 for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
180 if (!mRetainedTiles[i].mTextureHost) {
181 continue;
182 }
183 TextureHostOGL* texture = mRetainedTiles[i].mTextureHost->AsHostOGL();
184 if (!texture) {
185 continue;
186 }
187 texture->SetReleaseFence(new android::Fence(aReleaseFence->dup()));
188 }
189 }
190 #endif
192 TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
193 : ContentHost(aTextureInfo)
194 , mTiledBuffer(TiledLayerBufferComposite())
195 , mLowPrecisionTiledBuffer(TiledLayerBufferComposite())
196 , mOldTiledBuffer(TiledLayerBufferComposite())
197 , mOldLowPrecisionTiledBuffer(TiledLayerBufferComposite())
198 , mPendingUpload(false)
199 , mPendingLowPrecisionUpload(false)
200 {
201 MOZ_COUNT_CTOR(TiledContentHost);
202 }
204 TiledContentHost::~TiledContentHost()
205 {
206 MOZ_COUNT_DTOR(TiledContentHost);
208 // Unlock any buffers that may still be locked. If we have a pending upload,
209 // we will need to unlock the buffer that was about to be uploaded.
210 // If a buffer that was being composited had double-buffered tiles, we will
211 // need to unlock that buffer too.
212 if (mPendingUpload) {
213 mTiledBuffer.ReadUnlock();
214 if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
215 mOldTiledBuffer.ReadUnlock();
216 }
217 } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
218 mTiledBuffer.ReadUnlock();
219 }
221 if (mPendingLowPrecisionUpload) {
222 mLowPrecisionTiledBuffer.ReadUnlock();
223 if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
224 mOldLowPrecisionTiledBuffer.ReadUnlock();
225 }
226 } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
227 mLowPrecisionTiledBuffer.ReadUnlock();
228 }
229 }
231 void
232 TiledContentHost::Attach(Layer* aLayer,
233 Compositor* aCompositor,
234 AttachFlags aFlags /* = NO_FLAGS */)
235 {
236 CompositableHost::Attach(aLayer, aCompositor, aFlags);
237 static_cast<ThebesLayerComposite*>(aLayer)->EnsureTiled();
238 }
240 void
241 TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
242 const SurfaceDescriptorTiles& aTiledDescriptor)
243 {
244 if (aTiledDescriptor.resolution() < 1) {
245 if (mPendingLowPrecisionUpload) {
246 mLowPrecisionTiledBuffer.ReadUnlock();
247 } else {
248 mPendingLowPrecisionUpload = true;
249 // If the old buffer has double-buffered tiles, hang onto it so we can
250 // unlock it after we've composited the new buffer.
251 // We only need to hang onto the locks, but not the textures.
252 // Releasing the textures here can help prevent a memory spike in the
253 // situation that the client starts rendering new content before we get
254 // to composite the new buffer.
255 if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
256 mOldLowPrecisionTiledBuffer = mLowPrecisionTiledBuffer;
257 mOldLowPrecisionTiledBuffer.ReleaseTextureHosts();
258 }
259 }
260 mLowPrecisionTiledBuffer =
261 TiledLayerBufferComposite(aAllocator, aTiledDescriptor,
262 mLowPrecisionTiledBuffer.GetPaintedRegion());
263 } else {
264 if (mPendingUpload) {
265 mTiledBuffer.ReadUnlock();
266 } else {
267 mPendingUpload = true;
268 if (mTiledBuffer.HasDoubleBufferedTiles()) {
269 mOldTiledBuffer = mTiledBuffer;
270 mOldTiledBuffer.ReleaseTextureHosts();
271 }
272 }
273 mTiledBuffer = TiledLayerBufferComposite(aAllocator, aTiledDescriptor,
274 mTiledBuffer.GetPaintedRegion());
275 }
276 }
278 void
279 TiledContentHost::Composite(EffectChain& aEffectChain,
280 float aOpacity,
281 const gfx::Matrix4x4& aTransform,
282 const gfx::Filter& aFilter,
283 const gfx::Rect& aClipRect,
284 const nsIntRegion* aVisibleRegion /* = nullptr */,
285 TiledLayerProperties* aLayerProperties /* = nullptr */)
286 {
287 MOZ_ASSERT(aLayerProperties, "aLayerProperties required for TiledContentHost");
289 if (mPendingUpload) {
290 mTiledBuffer.SetCompositor(mCompositor);
291 mTiledBuffer.Upload();
293 // For a single-buffered tiled buffer, Upload will upload the shared memory
294 // surface to texture memory and we no longer need to read from them.
295 if (!mTiledBuffer.HasDoubleBufferedTiles()) {
296 mTiledBuffer.ReadUnlock();
297 }
298 }
299 if (mPendingLowPrecisionUpload) {
300 mLowPrecisionTiledBuffer.SetCompositor(mCompositor);
301 mLowPrecisionTiledBuffer.Upload();
303 if (!mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
304 mLowPrecisionTiledBuffer.ReadUnlock();
305 }
306 }
308 RenderLayerBuffer(mLowPrecisionTiledBuffer, aEffectChain, aOpacity, aFilter,
309 aClipRect, aLayerProperties->mVisibleRegion, aTransform);
310 RenderLayerBuffer(mTiledBuffer, aEffectChain, aOpacity, aFilter,
311 aClipRect, aLayerProperties->mVisibleRegion, aTransform);
313 // Now release the old buffer if it had double-buffered tiles, as we can
314 // guarantee that they're no longer on the screen (and so any locks that may
315 // have been held have been released).
316 if (mPendingUpload && mOldTiledBuffer.HasDoubleBufferedTiles()) {
317 mOldTiledBuffer.ReadUnlock();
318 mOldTiledBuffer = TiledLayerBufferComposite();
319 }
320 if (mPendingLowPrecisionUpload && mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
321 mOldLowPrecisionTiledBuffer.ReadUnlock();
322 mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
323 }
324 mPendingUpload = mPendingLowPrecisionUpload = false;
325 }
328 void
329 TiledContentHost::RenderTile(const TileHost& aTile,
330 EffectChain& aEffectChain,
331 float aOpacity,
332 const gfx::Matrix4x4& aTransform,
333 const gfx::Filter& aFilter,
334 const gfx::Rect& aClipRect,
335 const nsIntRegion& aScreenRegion,
336 const nsIntPoint& aTextureOffset,
337 const nsIntSize& aTextureBounds)
338 {
339 if (aTile.IsPlaceholderTile()) {
340 // This shouldn't ever happen, but let's fail semi-gracefully. No need
341 // to warn, the texture update would have already caught this.
342 return;
343 }
345 nsIntRect screenBounds = aScreenRegion.GetBounds();
346 Rect quad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
347 quad = aTransform.TransformBounds(quad);
349 if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(aClipRect))) {
350 return;
351 }
353 AutoLockTextureHost autoLock(aTile.mTextureHost);
354 if (autoLock.Failed()) {
355 NS_WARNING("Failed to lock tile");
356 return;
357 }
358 RefPtr<NewTextureSource> source = aTile.mTextureHost->GetTextureSources();
359 if (!source) {
360 return;
361 }
363 RefPtr<TexturedEffect> effect =
364 CreateTexturedEffect(aTile.mTextureHost->GetFormat(), source, aFilter);
365 if (!effect) {
366 return;
367 }
369 aEffectChain.mPrimaryEffect = effect;
371 nsIntRegionRectIterator it(aScreenRegion);
372 for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) {
373 Rect graphicsRect(rect->x, rect->y, rect->width, rect->height);
374 Rect textureRect(rect->x - aTextureOffset.x, rect->y - aTextureOffset.y,
375 rect->width, rect->height);
377 effect->mTextureCoords = Rect(textureRect.x / aTextureBounds.width,
378 textureRect.y / aTextureBounds.height,
379 textureRect.width / aTextureBounds.width,
380 textureRect.height / aTextureBounds.height);
381 mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform);
382 }
383 mCompositor->DrawDiagnostics(DIAGNOSTIC_CONTENT|DIAGNOSTIC_TILE,
384 aScreenRegion, aClipRect, aTransform, mFlashCounter);
385 }
387 void
388 TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
389 EffectChain& aEffectChain,
390 float aOpacity,
391 const gfx::Filter& aFilter,
392 const gfx::Rect& aClipRect,
393 nsIntRegion aVisibleRegion,
394 gfx::Matrix4x4 aTransform)
395 {
396 if (!mCompositor) {
397 NS_WARNING("Can't render tiled content host - no compositor");
398 return;
399 }
400 float resolution = aLayerBuffer.GetResolution();
401 gfx::Size layerScale(1, 1);
403 // We assume that the current frame resolution is the one used in our high
404 // precision layer buffer. Compensate for a changing frame resolution when
405 // rendering the low precision buffer.
406 if (aLayerBuffer.GetFrameResolution() != mTiledBuffer.GetFrameResolution()) {
407 const CSSToParentLayerScale& layerResolution = aLayerBuffer.GetFrameResolution();
408 const CSSToParentLayerScale& localResolution = mTiledBuffer.GetFrameResolution();
409 layerScale.width = layerScale.height = layerResolution.scale / localResolution.scale;
410 aVisibleRegion.ScaleRoundOut(layerScale.width, layerScale.height);
411 }
413 // If we're drawing the low precision buffer, make sure the high precision
414 // buffer is masked out to avoid overdraw and rendering artifacts with
415 // non-opaque layers.
416 nsIntRegion maskRegion;
417 if (resolution != mTiledBuffer.GetResolution()) {
418 maskRegion = mTiledBuffer.GetValidRegion();
419 // XXX This should be ScaleRoundIn, but there is no such function on
420 // nsIntRegion.
421 maskRegion.ScaleRoundOut(layerScale.width, layerScale.height);
422 }
424 // Make sure the resolution and difference in frame resolution are accounted
425 // for in the layer transform.
426 aTransform.Scale(1/(resolution * layerScale.width),
427 1/(resolution * layerScale.height), 1);
429 uint32_t rowCount = 0;
430 uint32_t tileX = 0;
431 nsIntRect visibleRect = aVisibleRegion.GetBounds();
432 gfx::IntSize scaledTileSize = aLayerBuffer.GetScaledTileSize();
433 for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
434 rowCount++;
435 int32_t tileStartX = aLayerBuffer.GetTileStart(x, scaledTileSize.width);
436 int32_t w = scaledTileSize.width - tileStartX;
437 if (x + w > visibleRect.x + visibleRect.width) {
438 w = visibleRect.x + visibleRect.width - x;
439 }
440 int tileY = 0;
441 for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
442 int32_t tileStartY = aLayerBuffer.GetTileStart(y, scaledTileSize.height);
443 int32_t h = scaledTileSize.height - tileStartY;
444 if (y + h > visibleRect.y + visibleRect.height) {
445 h = visibleRect.y + visibleRect.height - y;
446 }
448 TileHost tileTexture = aLayerBuffer.
449 GetTile(nsIntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
450 aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height)));
451 if (tileTexture != aLayerBuffer.GetPlaceholderTile()) {
452 nsIntRegion tileDrawRegion;
453 tileDrawRegion.And(nsIntRect(x, y, w, h), aLayerBuffer.GetValidRegion());
454 tileDrawRegion.And(tileDrawRegion, aVisibleRegion);
455 tileDrawRegion.Sub(tileDrawRegion, maskRegion);
457 if (!tileDrawRegion.IsEmpty()) {
458 tileDrawRegion.ScaleRoundOut(resolution, resolution);
459 nsIntPoint tileOffset((x - tileStartX) * resolution,
460 (y - tileStartY) * resolution);
461 gfx::IntSize tileSize = aLayerBuffer.GetTileSize();
462 RenderTile(tileTexture, aEffectChain, aOpacity, aTransform, aFilter, aClipRect, tileDrawRegion,
463 tileOffset, nsIntSize(tileSize.width, tileSize.height));
464 }
465 }
466 tileY++;
467 y += h;
468 }
469 tileX++;
470 x += w;
471 }
472 gfx::Rect rect(visibleRect.x, visibleRect.y,
473 visibleRect.width, visibleRect.height);
474 GetCompositor()->DrawDiagnostics(DIAGNOSTIC_CONTENT,
475 rect, aClipRect, aTransform, mFlashCounter);
476 }
478 void
479 TiledContentHost::PrintInfo(nsACString& aTo, const char* aPrefix)
480 {
481 aTo += aPrefix;
482 aTo += nsPrintfCString("TiledContentHost (0x%p)", this);
484 }
486 #ifdef MOZ_DUMP_PAINTING
487 void
488 TiledContentHost::Dump(FILE* aFile,
489 const char* aPrefix,
490 bool aDumpHtml)
491 {
492 if (!aFile) {
493 aFile = stderr;
494 }
496 TiledLayerBufferComposite::Iterator it = mTiledBuffer.TilesBegin();
497 TiledLayerBufferComposite::Iterator stop = mTiledBuffer.TilesEnd();
498 if (aDumpHtml) {
499 fprintf_stderr(aFile, "<ul>");
500 }
501 for (;it != stop; ++it) {
502 fprintf_stderr(aFile, "%s", aPrefix);
503 fprintf_stderr(aFile, aDumpHtml ? "<li> <a href=" : "Tile ");
504 if (it->IsPlaceholderTile()) {
505 fprintf_stderr(aFile, "empty tile");
506 } else {
507 DumpTextureHost(aFile, it->mTextureHost);
508 }
509 fprintf_stderr(aFile, aDumpHtml ? " >Tile</a></li>" : " ");
510 }
511 if (aDumpHtml) {
512 fprintf_stderr(aFile, "</ul>");
513 }
514 }
515 #endif
517 } // namespace
518 } // namespace