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.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "mozilla/layers/ContentHost.h" |
michael@0 | 7 | #include "LayersLogging.h" // for AppendToString |
michael@0 | 8 | #include "gfx2DGlue.h" // for ContentForFormat |
michael@0 | 9 | #include "mozilla/gfx/Point.h" // for IntSize |
michael@0 | 10 | #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc |
michael@0 | 11 | #include "mozilla/gfx/BaseRect.h" // for BaseRect |
michael@0 | 12 | #include "mozilla/layers/Compositor.h" // for Compositor |
michael@0 | 13 | #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc |
michael@0 | 14 | #include "mozilla/layers/LayersMessages.h" // for ThebesBufferData |
michael@0 | 15 | #include "nsAString.h" |
michael@0 | 16 | #include "nsPrintfCString.h" // for nsPrintfCString |
michael@0 | 17 | #include "nsString.h" // for nsAutoCString |
michael@0 | 18 | #include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL |
michael@0 | 19 | |
michael@0 | 20 | namespace mozilla { |
michael@0 | 21 | namespace gfx { |
michael@0 | 22 | class Matrix4x4; |
michael@0 | 23 | } |
michael@0 | 24 | using namespace gfx; |
michael@0 | 25 | |
michael@0 | 26 | namespace layers { |
michael@0 | 27 | |
michael@0 | 28 | ContentHostBase::ContentHostBase(const TextureInfo& aTextureInfo) |
michael@0 | 29 | : ContentHost(aTextureInfo) |
michael@0 | 30 | , mPaintWillResample(false) |
michael@0 | 31 | , mInitialised(false) |
michael@0 | 32 | {} |
michael@0 | 33 | |
michael@0 | 34 | ContentHostBase::~ContentHostBase() |
michael@0 | 35 | { |
michael@0 | 36 | } |
michael@0 | 37 | |
michael@0 | 38 | struct AutoLockContentHost |
michael@0 | 39 | { |
michael@0 | 40 | AutoLockContentHost(ContentHostBase* aHost) |
michael@0 | 41 | : mHost(aHost) |
michael@0 | 42 | { |
michael@0 | 43 | mSucceeded = mHost->Lock(); |
michael@0 | 44 | } |
michael@0 | 45 | |
michael@0 | 46 | ~AutoLockContentHost() |
michael@0 | 47 | { |
michael@0 | 48 | if (mSucceeded) { |
michael@0 | 49 | mHost->Unlock(); |
michael@0 | 50 | } |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | bool Failed() { return !mSucceeded; } |
michael@0 | 54 | |
michael@0 | 55 | ContentHostBase* mHost; |
michael@0 | 56 | bool mSucceeded; |
michael@0 | 57 | }; |
michael@0 | 58 | |
michael@0 | 59 | void |
michael@0 | 60 | ContentHostBase::Composite(EffectChain& aEffectChain, |
michael@0 | 61 | float aOpacity, |
michael@0 | 62 | const gfx::Matrix4x4& aTransform, |
michael@0 | 63 | const Filter& aFilter, |
michael@0 | 64 | const Rect& aClipRect, |
michael@0 | 65 | const nsIntRegion* aVisibleRegion, |
michael@0 | 66 | TiledLayerProperties* aLayerProperties) |
michael@0 | 67 | { |
michael@0 | 68 | NS_ASSERTION(aVisibleRegion, "Requires a visible region"); |
michael@0 | 69 | |
michael@0 | 70 | AutoLockContentHost lock(this); |
michael@0 | 71 | if (lock.Failed()) { |
michael@0 | 72 | return; |
michael@0 | 73 | } |
michael@0 | 74 | |
michael@0 | 75 | RefPtr<NewTextureSource> source = GetTextureSource(); |
michael@0 | 76 | RefPtr<NewTextureSource> sourceOnWhite = GetTextureSourceOnWhite(); |
michael@0 | 77 | |
michael@0 | 78 | if (!source) { |
michael@0 | 79 | return; |
michael@0 | 80 | } |
michael@0 | 81 | RefPtr<TexturedEffect> effect = |
michael@0 | 82 | CreateTexturedEffect(source, sourceOnWhite, aFilter); |
michael@0 | 83 | |
michael@0 | 84 | if (!effect) { |
michael@0 | 85 | return; |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | aEffectChain.mPrimaryEffect = effect; |
michael@0 | 89 | |
michael@0 | 90 | nsIntRegion tmpRegion; |
michael@0 | 91 | const nsIntRegion* renderRegion; |
michael@0 | 92 | if (PaintWillResample()) { |
michael@0 | 93 | // If we're resampling, then the texture image will contain exactly the |
michael@0 | 94 | // entire visible region's bounds, and we should draw it all in one quad |
michael@0 | 95 | // to avoid unexpected aliasing. |
michael@0 | 96 | tmpRegion = aVisibleRegion->GetBounds(); |
michael@0 | 97 | renderRegion = &tmpRegion; |
michael@0 | 98 | } else { |
michael@0 | 99 | renderRegion = aVisibleRegion; |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | nsIntRegion region(*renderRegion); |
michael@0 | 103 | nsIntPoint origin = GetOriginOffset(); |
michael@0 | 104 | // translate into TexImage space, buffer origin might not be at texture (0,0) |
michael@0 | 105 | region.MoveBy(-origin); |
michael@0 | 106 | |
michael@0 | 107 | // Figure out the intersecting draw region |
michael@0 | 108 | gfx::IntSize texSize = source->GetSize(); |
michael@0 | 109 | nsIntRect textureRect = nsIntRect(0, 0, texSize.width, texSize.height); |
michael@0 | 110 | textureRect.MoveBy(region.GetBounds().TopLeft()); |
michael@0 | 111 | nsIntRegion subregion; |
michael@0 | 112 | subregion.And(region, textureRect); |
michael@0 | 113 | if (subregion.IsEmpty()) { |
michael@0 | 114 | // Region is empty, nothing to draw |
michael@0 | 115 | return; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | nsIntRegion screenRects; |
michael@0 | 119 | nsIntRegion regionRects; |
michael@0 | 120 | |
michael@0 | 121 | // Collect texture/screen coordinates for drawing |
michael@0 | 122 | nsIntRegionRectIterator iter(subregion); |
michael@0 | 123 | while (const nsIntRect* iterRect = iter.Next()) { |
michael@0 | 124 | nsIntRect regionRect = *iterRect; |
michael@0 | 125 | nsIntRect screenRect = regionRect; |
michael@0 | 126 | screenRect.MoveBy(origin); |
michael@0 | 127 | |
michael@0 | 128 | screenRects.Or(screenRects, screenRect); |
michael@0 | 129 | regionRects.Or(regionRects, regionRect); |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | TileIterator* tileIter = source->AsTileIterator(); |
michael@0 | 133 | TileIterator* iterOnWhite = nullptr; |
michael@0 | 134 | if (tileIter) { |
michael@0 | 135 | tileIter->BeginTileIteration(); |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | if (sourceOnWhite) { |
michael@0 | 139 | iterOnWhite = sourceOnWhite->AsTileIterator(); |
michael@0 | 140 | MOZ_ASSERT(!tileIter || tileIter->GetTileCount() == iterOnWhite->GetTileCount(), |
michael@0 | 141 | "Tile count mismatch on component alpha texture"); |
michael@0 | 142 | if (iterOnWhite) { |
michael@0 | 143 | iterOnWhite->BeginTileIteration(); |
michael@0 | 144 | } |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | bool usingTiles = (tileIter && tileIter->GetTileCount() > 1); |
michael@0 | 148 | do { |
michael@0 | 149 | if (iterOnWhite) { |
michael@0 | 150 | MOZ_ASSERT(iterOnWhite->GetTileRect() == tileIter->GetTileRect(), |
michael@0 | 151 | "component alpha textures should be the same size."); |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | nsIntRect texRect = tileIter ? tileIter->GetTileRect() |
michael@0 | 155 | : nsIntRect(0, 0, |
michael@0 | 156 | texSize.width, |
michael@0 | 157 | texSize.height); |
michael@0 | 158 | |
michael@0 | 159 | // Draw texture. If we're using tiles, we do repeating manually, as texture |
michael@0 | 160 | // repeat would cause each individual tile to repeat instead of the |
michael@0 | 161 | // compound texture as a whole. This involves drawing at most 4 sections, |
michael@0 | 162 | // 2 for each axis that has texture repeat. |
michael@0 | 163 | for (int y = 0; y < (usingTiles ? 2 : 1); y++) { |
michael@0 | 164 | for (int x = 0; x < (usingTiles ? 2 : 1); x++) { |
michael@0 | 165 | nsIntRect currentTileRect(texRect); |
michael@0 | 166 | currentTileRect.MoveBy(x * texSize.width, y * texSize.height); |
michael@0 | 167 | |
michael@0 | 168 | nsIntRegionRectIterator screenIter(screenRects); |
michael@0 | 169 | nsIntRegionRectIterator regionIter(regionRects); |
michael@0 | 170 | |
michael@0 | 171 | const nsIntRect* screenRect; |
michael@0 | 172 | const nsIntRect* regionRect; |
michael@0 | 173 | while ((screenRect = screenIter.Next()) && |
michael@0 | 174 | (regionRect = regionIter.Next())) { |
michael@0 | 175 | nsIntRect tileScreenRect(*screenRect); |
michael@0 | 176 | nsIntRect tileRegionRect(*regionRect); |
michael@0 | 177 | |
michael@0 | 178 | // When we're using tiles, find the intersection between the tile |
michael@0 | 179 | // rect and this region rect. Tiling is then handled by the |
michael@0 | 180 | // outer for-loops and modifying the tile rect. |
michael@0 | 181 | if (usingTiles) { |
michael@0 | 182 | tileScreenRect.MoveBy(-origin); |
michael@0 | 183 | tileScreenRect = tileScreenRect.Intersect(currentTileRect); |
michael@0 | 184 | tileScreenRect.MoveBy(origin); |
michael@0 | 185 | |
michael@0 | 186 | if (tileScreenRect.IsEmpty()) |
michael@0 | 187 | continue; |
michael@0 | 188 | |
michael@0 | 189 | tileRegionRect = regionRect->Intersect(currentTileRect); |
michael@0 | 190 | tileRegionRect.MoveBy(-currentTileRect.TopLeft()); |
michael@0 | 191 | } |
michael@0 | 192 | gfx::Rect rect(tileScreenRect.x, tileScreenRect.y, |
michael@0 | 193 | tileScreenRect.width, tileScreenRect.height); |
michael@0 | 194 | |
michael@0 | 195 | effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width, |
michael@0 | 196 | Float(tileRegionRect.y) / texRect.height, |
michael@0 | 197 | Float(tileRegionRect.width) / texRect.width, |
michael@0 | 198 | Float(tileRegionRect.height) / texRect.height); |
michael@0 | 199 | GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform); |
michael@0 | 200 | if (usingTiles) { |
michael@0 | 201 | DiagnosticTypes diagnostics = DIAGNOSTIC_CONTENT | DIAGNOSTIC_BIGIMAGE; |
michael@0 | 202 | diagnostics |= iterOnWhite ? DIAGNOSTIC_COMPONENT_ALPHA : 0; |
michael@0 | 203 | GetCompositor()->DrawDiagnostics(diagnostics, rect, aClipRect, |
michael@0 | 204 | aTransform, mFlashCounter); |
michael@0 | 205 | } |
michael@0 | 206 | } |
michael@0 | 207 | } |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | if (iterOnWhite) { |
michael@0 | 211 | iterOnWhite->NextTile(); |
michael@0 | 212 | } |
michael@0 | 213 | } while (usingTiles && tileIter->NextTile()); |
michael@0 | 214 | |
michael@0 | 215 | if (tileIter) { |
michael@0 | 216 | tileIter->EndTileIteration(); |
michael@0 | 217 | } |
michael@0 | 218 | if (iterOnWhite) { |
michael@0 | 219 | iterOnWhite->EndTileIteration(); |
michael@0 | 220 | } |
michael@0 | 221 | |
michael@0 | 222 | DiagnosticTypes diagnostics = DIAGNOSTIC_CONTENT; |
michael@0 | 223 | diagnostics |= iterOnWhite ? DIAGNOSTIC_COMPONENT_ALPHA : 0; |
michael@0 | 224 | GetCompositor()->DrawDiagnostics(diagnostics, *aVisibleRegion, aClipRect, |
michael@0 | 225 | aTransform, mFlashCounter); |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | |
michael@0 | 229 | void |
michael@0 | 230 | ContentHostTexture::UseTextureHost(TextureHost* aTexture) |
michael@0 | 231 | { |
michael@0 | 232 | ContentHostBase::UseTextureHost(aTexture); |
michael@0 | 233 | mTextureHost = aTexture; |
michael@0 | 234 | mTextureHostOnWhite = nullptr; |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | void |
michael@0 | 238 | ContentHostTexture::UseComponentAlphaTextures(TextureHost* aTextureOnBlack, |
michael@0 | 239 | TextureHost* aTextureOnWhite) |
michael@0 | 240 | { |
michael@0 | 241 | ContentHostBase::UseComponentAlphaTextures(aTextureOnBlack, aTextureOnWhite); |
michael@0 | 242 | mTextureHost = aTextureOnBlack; |
michael@0 | 243 | mTextureHostOnWhite = aTextureOnWhite; |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | void |
michael@0 | 247 | ContentHostTexture::SetCompositor(Compositor* aCompositor) |
michael@0 | 248 | { |
michael@0 | 249 | ContentHostBase::SetCompositor(aCompositor); |
michael@0 | 250 | if (mTextureHost) { |
michael@0 | 251 | mTextureHost->SetCompositor(aCompositor); |
michael@0 | 252 | } |
michael@0 | 253 | if (mTextureHostOnWhite) { |
michael@0 | 254 | mTextureHostOnWhite->SetCompositor(aCompositor); |
michael@0 | 255 | } |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 259 | void |
michael@0 | 260 | ContentHostTexture::Dump(FILE* aFile, |
michael@0 | 261 | const char* aPrefix, |
michael@0 | 262 | bool aDumpHtml) |
michael@0 | 263 | { |
michael@0 | 264 | if (!aDumpHtml) { |
michael@0 | 265 | return; |
michael@0 | 266 | } |
michael@0 | 267 | if (!aFile) { |
michael@0 | 268 | aFile = stderr; |
michael@0 | 269 | } |
michael@0 | 270 | fprintf(aFile, "<ul>"); |
michael@0 | 271 | if (mTextureHost) { |
michael@0 | 272 | fprintf(aFile, "%s", aPrefix); |
michael@0 | 273 | fprintf(aFile, "<li> <a href="); |
michael@0 | 274 | DumpTextureHost(aFile, mTextureHost); |
michael@0 | 275 | fprintf(aFile, "> Front buffer </a></li> "); |
michael@0 | 276 | } |
michael@0 | 277 | if (mTextureHostOnWhite) { |
michael@0 | 278 | fprintf(aFile, "%s", aPrefix); |
michael@0 | 279 | fprintf(aFile, "<li> <a href="); |
michael@0 | 280 | DumpTextureHost(aFile, mTextureHostOnWhite); |
michael@0 | 281 | fprintf(aFile, "> Front buffer on white </a> </li> "); |
michael@0 | 282 | } |
michael@0 | 283 | fprintf(aFile, "</ul>"); |
michael@0 | 284 | } |
michael@0 | 285 | #endif |
michael@0 | 286 | |
michael@0 | 287 | static inline void |
michael@0 | 288 | AddWrappedRegion(const nsIntRegion& aInput, nsIntRegion& aOutput, |
michael@0 | 289 | const nsIntSize& aSize, const nsIntPoint& aShift) |
michael@0 | 290 | { |
michael@0 | 291 | nsIntRegion tempRegion; |
michael@0 | 292 | tempRegion.And(nsIntRect(aShift, aSize), aInput); |
michael@0 | 293 | tempRegion.MoveBy(-aShift); |
michael@0 | 294 | aOutput.Or(aOutput, tempRegion); |
michael@0 | 295 | } |
michael@0 | 296 | |
michael@0 | 297 | bool |
michael@0 | 298 | ContentHostSingleBuffered::UpdateThebes(const ThebesBufferData& aData, |
michael@0 | 299 | const nsIntRegion& aUpdated, |
michael@0 | 300 | const nsIntRegion& aOldValidRegionBack, |
michael@0 | 301 | nsIntRegion* aUpdatedRegionBack) |
michael@0 | 302 | { |
michael@0 | 303 | aUpdatedRegionBack->SetEmpty(); |
michael@0 | 304 | |
michael@0 | 305 | if (!mTextureHost) { |
michael@0 | 306 | mInitialised = false; |
michael@0 | 307 | return true; // FIXME should we return false? Returning true for now |
michael@0 | 308 | } // to preserve existing behavior of NOT causing IPC errors. |
michael@0 | 309 | |
michael@0 | 310 | // updated is in screen coordinates. Convert it to buffer coordinates. |
michael@0 | 311 | nsIntRegion destRegion(aUpdated); |
michael@0 | 312 | destRegion.MoveBy(-aData.rect().TopLeft()); |
michael@0 | 313 | |
michael@0 | 314 | if (!aData.rect().Contains(aUpdated.GetBounds()) || |
michael@0 | 315 | aData.rotation().x > aData.rect().width || |
michael@0 | 316 | aData.rotation().y > aData.rect().height) { |
michael@0 | 317 | NS_ERROR("Invalid update data"); |
michael@0 | 318 | return false; |
michael@0 | 319 | } |
michael@0 | 320 | |
michael@0 | 321 | // destRegion is now in logical coordinates relative to the buffer, but we |
michael@0 | 322 | // need to account for rotation. We do that by moving the region to the |
michael@0 | 323 | // rotation offset and then wrapping any pixels that extend off the |
michael@0 | 324 | // bottom/right edges. |
michael@0 | 325 | |
michael@0 | 326 | // Shift to the rotation point |
michael@0 | 327 | destRegion.MoveBy(aData.rotation()); |
michael@0 | 328 | |
michael@0 | 329 | nsIntSize bufferSize = aData.rect().Size(); |
michael@0 | 330 | |
michael@0 | 331 | // Select only the pixels that are still within the buffer. |
michael@0 | 332 | nsIntRegion finalRegion; |
michael@0 | 333 | finalRegion.And(nsIntRect(nsIntPoint(), bufferSize), destRegion); |
michael@0 | 334 | |
michael@0 | 335 | // For each of the overlap areas (right, bottom-right, bottom), select those |
michael@0 | 336 | // pixels and wrap them around to the opposite edge of the buffer rect. |
michael@0 | 337 | AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, 0)); |
michael@0 | 338 | AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(aData.rect().width, aData.rect().height)); |
michael@0 | 339 | AddWrappedRegion(destRegion, finalRegion, bufferSize, nsIntPoint(0, aData.rect().height)); |
michael@0 | 340 | |
michael@0 | 341 | MOZ_ASSERT(nsIntRect(0, 0, aData.rect().width, aData.rect().height).Contains(finalRegion.GetBounds())); |
michael@0 | 342 | |
michael@0 | 343 | mTextureHost->Updated(&finalRegion); |
michael@0 | 344 | if (mTextureHostOnWhite) { |
michael@0 | 345 | mTextureHostOnWhite->Updated(&finalRegion); |
michael@0 | 346 | } |
michael@0 | 347 | mInitialised = true; |
michael@0 | 348 | |
michael@0 | 349 | mBufferRect = aData.rect(); |
michael@0 | 350 | mBufferRotation = aData.rotation(); |
michael@0 | 351 | |
michael@0 | 352 | return true; |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | bool |
michael@0 | 356 | ContentHostDoubleBuffered::UpdateThebes(const ThebesBufferData& aData, |
michael@0 | 357 | const nsIntRegion& aUpdated, |
michael@0 | 358 | const nsIntRegion& aOldValidRegionBack, |
michael@0 | 359 | nsIntRegion* aUpdatedRegionBack) |
michael@0 | 360 | { |
michael@0 | 361 | if (!mTextureHost) { |
michael@0 | 362 | mInitialised = false; |
michael@0 | 363 | |
michael@0 | 364 | *aUpdatedRegionBack = aUpdated; |
michael@0 | 365 | return true; |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | // We don't need to calculate an update region because we assume that if we |
michael@0 | 369 | // are using double buffering then we have render-to-texture and thus no |
michael@0 | 370 | // upload to do. |
michael@0 | 371 | mTextureHost->Updated(); |
michael@0 | 372 | if (mTextureHostOnWhite) { |
michael@0 | 373 | mTextureHostOnWhite->Updated(); |
michael@0 | 374 | } |
michael@0 | 375 | mInitialised = true; |
michael@0 | 376 | |
michael@0 | 377 | mBufferRect = aData.rect(); |
michael@0 | 378 | mBufferRotation = aData.rotation(); |
michael@0 | 379 | |
michael@0 | 380 | *aUpdatedRegionBack = aUpdated; |
michael@0 | 381 | |
michael@0 | 382 | // Save the current valid region of our front buffer, because if |
michael@0 | 383 | // we're double buffering, it's going to be the valid region for the |
michael@0 | 384 | // next back buffer sent back to the renderer. |
michael@0 | 385 | // |
michael@0 | 386 | // NB: we rely here on the fact that mValidRegion is initialized to |
michael@0 | 387 | // empty, and that the first time Swap() is called we don't have a |
michael@0 | 388 | // valid front buffer that we're going to return to content. |
michael@0 | 389 | mValidRegionForNextBackBuffer = aOldValidRegionBack; |
michael@0 | 390 | |
michael@0 | 391 | return true; |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | ContentHostIncremental::ContentHostIncremental(const TextureInfo& aTextureInfo) |
michael@0 | 395 | : ContentHostBase(aTextureInfo) |
michael@0 | 396 | , mDeAllocator(nullptr) |
michael@0 | 397 | , mLocked(false) |
michael@0 | 398 | { |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | ContentHostIncremental::~ContentHostIncremental() |
michael@0 | 402 | { |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | bool |
michael@0 | 406 | ContentHostIncremental::CreatedIncrementalTexture(ISurfaceAllocator* aAllocator, |
michael@0 | 407 | const TextureInfo& aTextureInfo, |
michael@0 | 408 | const nsIntRect& aBufferRect) |
michael@0 | 409 | { |
michael@0 | 410 | mUpdateList.AppendElement(new TextureCreationRequest(aTextureInfo, |
michael@0 | 411 | aBufferRect)); |
michael@0 | 412 | mDeAllocator = aAllocator; |
michael@0 | 413 | FlushUpdateQueue(); |
michael@0 | 414 | return true; |
michael@0 | 415 | } |
michael@0 | 416 | |
michael@0 | 417 | void |
michael@0 | 418 | ContentHostIncremental::UpdateIncremental(TextureIdentifier aTextureId, |
michael@0 | 419 | SurfaceDescriptor& aSurface, |
michael@0 | 420 | const nsIntRegion& aUpdated, |
michael@0 | 421 | const nsIntRect& aBufferRect, |
michael@0 | 422 | const nsIntPoint& aBufferRotation) |
michael@0 | 423 | { |
michael@0 | 424 | mUpdateList.AppendElement(new TextureUpdateRequest(mDeAllocator, |
michael@0 | 425 | aTextureId, |
michael@0 | 426 | aSurface, |
michael@0 | 427 | aUpdated, |
michael@0 | 428 | aBufferRect, |
michael@0 | 429 | aBufferRotation)); |
michael@0 | 430 | FlushUpdateQueue(); |
michael@0 | 431 | } |
michael@0 | 432 | |
michael@0 | 433 | void |
michael@0 | 434 | ContentHostIncremental::FlushUpdateQueue() |
michael@0 | 435 | { |
michael@0 | 436 | // If we're not compositing for some reason (the window being minimized |
michael@0 | 437 | // is one example), then we never process these updates and it can consume |
michael@0 | 438 | // huge amounts of memory. Instead we forcibly process the updates (during the |
michael@0 | 439 | // transaction) if the list gets too long. |
michael@0 | 440 | static const uint32_t kMaxUpdateCount = 6; |
michael@0 | 441 | if (mUpdateList.Length() >= kMaxUpdateCount) { |
michael@0 | 442 | ProcessTextureUpdates(); |
michael@0 | 443 | } |
michael@0 | 444 | } |
michael@0 | 445 | |
michael@0 | 446 | void |
michael@0 | 447 | ContentHostIncremental::ProcessTextureUpdates() |
michael@0 | 448 | { |
michael@0 | 449 | for (uint32_t i = 0; i < mUpdateList.Length(); i++) { |
michael@0 | 450 | mUpdateList[i]->Execute(this); |
michael@0 | 451 | } |
michael@0 | 452 | mUpdateList.Clear(); |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | NewTextureSource* |
michael@0 | 456 | ContentHostIncremental::GetTextureSource() |
michael@0 | 457 | { |
michael@0 | 458 | MOZ_ASSERT(mLocked); |
michael@0 | 459 | return mSource; |
michael@0 | 460 | } |
michael@0 | 461 | |
michael@0 | 462 | NewTextureSource* |
michael@0 | 463 | ContentHostIncremental::GetTextureSourceOnWhite() |
michael@0 | 464 | { |
michael@0 | 465 | MOZ_ASSERT(mLocked); |
michael@0 | 466 | return mSourceOnWhite; |
michael@0 | 467 | } |
michael@0 | 468 | |
michael@0 | 469 | void |
michael@0 | 470 | ContentHostIncremental::TextureCreationRequest::Execute(ContentHostIncremental* aHost) |
michael@0 | 471 | { |
michael@0 | 472 | Compositor* compositor = aHost->GetCompositor(); |
michael@0 | 473 | MOZ_ASSERT(compositor); |
michael@0 | 474 | |
michael@0 | 475 | RefPtr<DataTextureSource> temp = |
michael@0 | 476 | compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags); |
michael@0 | 477 | MOZ_ASSERT(temp->AsSourceOGL() && |
michael@0 | 478 | temp->AsSourceOGL()->AsTextureImageTextureSource()); |
michael@0 | 479 | RefPtr<TextureImageTextureSourceOGL> newSource = |
michael@0 | 480 | temp->AsSourceOGL()->AsTextureImageTextureSource(); |
michael@0 | 481 | |
michael@0 | 482 | RefPtr<TextureImageTextureSourceOGL> newSourceOnWhite; |
michael@0 | 483 | if (mTextureInfo.mTextureFlags & TEXTURE_COMPONENT_ALPHA) { |
michael@0 | 484 | temp = |
michael@0 | 485 | compositor->CreateDataTextureSource(mTextureInfo.mTextureFlags); |
michael@0 | 486 | MOZ_ASSERT(temp->AsSourceOGL() && |
michael@0 | 487 | temp->AsSourceOGL()->AsTextureImageTextureSource()); |
michael@0 | 488 | newSourceOnWhite = temp->AsSourceOGL()->AsTextureImageTextureSource(); |
michael@0 | 489 | } |
michael@0 | 490 | |
michael@0 | 491 | if (mTextureInfo.mDeprecatedTextureHostFlags & TEXTURE_HOST_COPY_PREVIOUS) { |
michael@0 | 492 | MOZ_ASSERT(aHost->mSource); |
michael@0 | 493 | MOZ_ASSERT(aHost->mSource->IsValid()); |
michael@0 | 494 | nsIntRect bufferRect = aHost->mBufferRect; |
michael@0 | 495 | nsIntPoint bufferRotation = aHost->mBufferRotation; |
michael@0 | 496 | nsIntRect overlap; |
michael@0 | 497 | |
michael@0 | 498 | // The buffer looks like: |
michael@0 | 499 | // ______ |
michael@0 | 500 | // |1 |2 | Where the center point is offset by mBufferRotation from the top-left corner. |
michael@0 | 501 | // |___|__| |
michael@0 | 502 | // |3 |4 | |
michael@0 | 503 | // |___|__| |
michael@0 | 504 | // |
michael@0 | 505 | // This is drawn to the screen as: |
michael@0 | 506 | // ______ |
michael@0 | 507 | // |4 |3 | Where the center point is { width - mBufferRotation.x, height - mBufferRotation.y } from |
michael@0 | 508 | // |___|__| from the top left corner - rotationPoint. |
michael@0 | 509 | // |2 |1 | |
michael@0 | 510 | // |___|__| |
michael@0 | 511 | // |
michael@0 | 512 | |
michael@0 | 513 | // The basic idea below is to take all quadrant rectangles from the src and transform them into rectangles |
michael@0 | 514 | // in the destination. Unfortunately, it seems it is overly complex and could perhaps be simplified. |
michael@0 | 515 | |
michael@0 | 516 | nsIntRect srcBufferSpaceBottomRight(bufferRotation.x, bufferRotation.y, bufferRect.width - bufferRotation.x, bufferRect.height - bufferRotation.y); |
michael@0 | 517 | nsIntRect srcBufferSpaceTopRight(bufferRotation.x, 0, bufferRect.width - bufferRotation.x, bufferRotation.y); |
michael@0 | 518 | nsIntRect srcBufferSpaceTopLeft(0, 0, bufferRotation.x, bufferRotation.y); |
michael@0 | 519 | nsIntRect srcBufferSpaceBottomLeft(0, bufferRotation.y, bufferRotation.x, bufferRect.height - bufferRotation.y); |
michael@0 | 520 | |
michael@0 | 521 | overlap.IntersectRect(bufferRect, mBufferRect); |
michael@0 | 522 | |
michael@0 | 523 | nsIntRect srcRect(overlap), dstRect(overlap); |
michael@0 | 524 | srcRect.MoveBy(- bufferRect.TopLeft() + bufferRotation); |
michael@0 | 525 | |
michael@0 | 526 | nsIntRect srcRectDrawTopRight(srcRect); |
michael@0 | 527 | nsIntRect srcRectDrawTopLeft(srcRect); |
michael@0 | 528 | nsIntRect srcRectDrawBottomLeft(srcRect); |
michael@0 | 529 | // transform into the different quadrants |
michael@0 | 530 | srcRectDrawTopRight .MoveBy(-nsIntPoint(0, bufferRect.height)); |
michael@0 | 531 | srcRectDrawTopLeft .MoveBy(-nsIntPoint(bufferRect.width, bufferRect.height)); |
michael@0 | 532 | srcRectDrawBottomLeft.MoveBy(-nsIntPoint(bufferRect.width, 0)); |
michael@0 | 533 | |
michael@0 | 534 | // Intersect with the quadrant |
michael@0 | 535 | srcRect = srcRect .Intersect(srcBufferSpaceBottomRight); |
michael@0 | 536 | srcRectDrawTopRight = srcRectDrawTopRight .Intersect(srcBufferSpaceTopRight); |
michael@0 | 537 | srcRectDrawTopLeft = srcRectDrawTopLeft .Intersect(srcBufferSpaceTopLeft); |
michael@0 | 538 | srcRectDrawBottomLeft = srcRectDrawBottomLeft.Intersect(srcBufferSpaceBottomLeft); |
michael@0 | 539 | |
michael@0 | 540 | dstRect = srcRect; |
michael@0 | 541 | nsIntRect dstRectDrawTopRight(srcRectDrawTopRight); |
michael@0 | 542 | nsIntRect dstRectDrawTopLeft(srcRectDrawTopLeft); |
michael@0 | 543 | nsIntRect dstRectDrawBottomLeft(srcRectDrawBottomLeft); |
michael@0 | 544 | |
michael@0 | 545 | // transform back to src buffer space |
michael@0 | 546 | dstRect .MoveBy(-bufferRotation); |
michael@0 | 547 | dstRectDrawTopRight .MoveBy(-bufferRotation + nsIntPoint(0, bufferRect.height)); |
michael@0 | 548 | dstRectDrawTopLeft .MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, bufferRect.height)); |
michael@0 | 549 | dstRectDrawBottomLeft.MoveBy(-bufferRotation + nsIntPoint(bufferRect.width, 0)); |
michael@0 | 550 | |
michael@0 | 551 | // transform back to draw coordinates |
michael@0 | 552 | dstRect .MoveBy(bufferRect.TopLeft()); |
michael@0 | 553 | dstRectDrawTopRight .MoveBy(bufferRect.TopLeft()); |
michael@0 | 554 | dstRectDrawTopLeft .MoveBy(bufferRect.TopLeft()); |
michael@0 | 555 | dstRectDrawBottomLeft.MoveBy(bufferRect.TopLeft()); |
michael@0 | 556 | |
michael@0 | 557 | // transform to destBuffer space |
michael@0 | 558 | dstRect .MoveBy(-mBufferRect.TopLeft()); |
michael@0 | 559 | dstRectDrawTopRight .MoveBy(-mBufferRect.TopLeft()); |
michael@0 | 560 | dstRectDrawTopLeft .MoveBy(-mBufferRect.TopLeft()); |
michael@0 | 561 | dstRectDrawBottomLeft.MoveBy(-mBufferRect.TopLeft()); |
michael@0 | 562 | |
michael@0 | 563 | newSource->EnsureBuffer(mBufferRect.Size(), |
michael@0 | 564 | ContentForFormat(aHost->mSource->GetFormat())); |
michael@0 | 565 | |
michael@0 | 566 | aHost->mSource->CopyTo(srcRect, newSource, dstRect); |
michael@0 | 567 | if (bufferRotation != nsIntPoint(0, 0)) { |
michael@0 | 568 | // Draw the remaining quadrants. We call BlitTextureImage 3 extra |
michael@0 | 569 | // times instead of doing a single draw call because supporting that |
michael@0 | 570 | // with a tiled source is quite tricky. |
michael@0 | 571 | |
michael@0 | 572 | if (!srcRectDrawTopRight.IsEmpty()) |
michael@0 | 573 | aHost->mSource->CopyTo(srcRectDrawTopRight, |
michael@0 | 574 | newSource, dstRectDrawTopRight); |
michael@0 | 575 | if (!srcRectDrawTopLeft.IsEmpty()) |
michael@0 | 576 | aHost->mSource->CopyTo(srcRectDrawTopLeft, |
michael@0 | 577 | newSource, dstRectDrawTopLeft); |
michael@0 | 578 | if (!srcRectDrawBottomLeft.IsEmpty()) |
michael@0 | 579 | aHost->mSource->CopyTo(srcRectDrawBottomLeft, |
michael@0 | 580 | newSource, dstRectDrawBottomLeft); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | if (newSourceOnWhite) { |
michael@0 | 584 | newSourceOnWhite->EnsureBuffer(mBufferRect.Size(), |
michael@0 | 585 | ContentForFormat(aHost->mSourceOnWhite->GetFormat())); |
michael@0 | 586 | aHost->mSourceOnWhite->CopyTo(srcRect, newSourceOnWhite, dstRect); |
michael@0 | 587 | if (bufferRotation != nsIntPoint(0, 0)) { |
michael@0 | 588 | // draw the remaining quadrants |
michael@0 | 589 | if (!srcRectDrawTopRight.IsEmpty()) |
michael@0 | 590 | aHost->mSourceOnWhite->CopyTo(srcRectDrawTopRight, |
michael@0 | 591 | newSourceOnWhite, dstRectDrawTopRight); |
michael@0 | 592 | if (!srcRectDrawTopLeft.IsEmpty()) |
michael@0 | 593 | aHost->mSourceOnWhite->CopyTo(srcRectDrawTopLeft, |
michael@0 | 594 | newSourceOnWhite, dstRectDrawTopLeft); |
michael@0 | 595 | if (!srcRectDrawBottomLeft.IsEmpty()) |
michael@0 | 596 | aHost->mSourceOnWhite->CopyTo(srcRectDrawBottomLeft, |
michael@0 | 597 | newSourceOnWhite, dstRectDrawBottomLeft); |
michael@0 | 598 | } |
michael@0 | 599 | } |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | aHost->mSource = newSource; |
michael@0 | 603 | aHost->mSourceOnWhite = newSourceOnWhite; |
michael@0 | 604 | |
michael@0 | 605 | aHost->mBufferRect = mBufferRect; |
michael@0 | 606 | aHost->mBufferRotation = nsIntPoint(); |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | nsIntRect |
michael@0 | 610 | ContentHostIncremental::TextureUpdateRequest::GetQuadrantRectangle(XSide aXSide, |
michael@0 | 611 | YSide aYSide) const |
michael@0 | 612 | { |
michael@0 | 613 | // quadrantTranslation is the amount we translate the top-left |
michael@0 | 614 | // of the quadrant by to get coordinates relative to the layer |
michael@0 | 615 | nsIntPoint quadrantTranslation = -mBufferRotation; |
michael@0 | 616 | quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0; |
michael@0 | 617 | quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0; |
michael@0 | 618 | return mBufferRect + quadrantTranslation; |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | void |
michael@0 | 622 | ContentHostIncremental::TextureUpdateRequest::Execute(ContentHostIncremental* aHost) |
michael@0 | 623 | { |
michael@0 | 624 | nsIntRect drawBounds = mUpdated.GetBounds(); |
michael@0 | 625 | |
michael@0 | 626 | aHost->mBufferRect = mBufferRect; |
michael@0 | 627 | aHost->mBufferRotation = mBufferRotation; |
michael@0 | 628 | |
michael@0 | 629 | // Figure out which quadrant to draw in |
michael@0 | 630 | int32_t xBoundary = mBufferRect.XMost() - mBufferRotation.x; |
michael@0 | 631 | int32_t yBoundary = mBufferRect.YMost() - mBufferRotation.y; |
michael@0 | 632 | XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT; |
michael@0 | 633 | YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP; |
michael@0 | 634 | nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY); |
michael@0 | 635 | NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants"); |
michael@0 | 636 | |
michael@0 | 637 | mUpdated.MoveBy(-nsIntPoint(quadrantRect.x, quadrantRect.y)); |
michael@0 | 638 | |
michael@0 | 639 | IntPoint offset = ToIntPoint(-mUpdated.GetBounds().TopLeft()); |
michael@0 | 640 | |
michael@0 | 641 | RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(mDescriptor); |
michael@0 | 642 | |
michael@0 | 643 | if (mTextureId == TextureFront) { |
michael@0 | 644 | aHost->mSource->Update(surf, &mUpdated, &offset); |
michael@0 | 645 | } else { |
michael@0 | 646 | aHost->mSourceOnWhite->Update(surf, &mUpdated, &offset); |
michael@0 | 647 | } |
michael@0 | 648 | } |
michael@0 | 649 | |
michael@0 | 650 | void |
michael@0 | 651 | ContentHostTexture::PrintInfo(nsACString& aTo, const char* aPrefix) |
michael@0 | 652 | { |
michael@0 | 653 | aTo += aPrefix; |
michael@0 | 654 | aTo += nsPrintfCString("ContentHost (0x%p)", this); |
michael@0 | 655 | |
michael@0 | 656 | AppendToString(aTo, mBufferRect, " [buffer-rect=", "]"); |
michael@0 | 657 | AppendToString(aTo, mBufferRotation, " [buffer-rotation=", "]"); |
michael@0 | 658 | if (PaintWillResample()) { |
michael@0 | 659 | aTo += " [paint-will-resample]"; |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | nsAutoCString pfx(aPrefix); |
michael@0 | 663 | pfx += " "; |
michael@0 | 664 | |
michael@0 | 665 | if (mTextureHost) { |
michael@0 | 666 | aTo += "\n"; |
michael@0 | 667 | mTextureHost->PrintInfo(aTo, pfx.get()); |
michael@0 | 668 | } |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | |
michael@0 | 672 | LayerRenderState |
michael@0 | 673 | ContentHostTexture::GetRenderState() |
michael@0 | 674 | { |
michael@0 | 675 | if (!mTextureHost) { |
michael@0 | 676 | return LayerRenderState(); |
michael@0 | 677 | } |
michael@0 | 678 | |
michael@0 | 679 | LayerRenderState result = mTextureHost->GetRenderState(); |
michael@0 | 680 | |
michael@0 | 681 | if (mBufferRotation != nsIntPoint()) { |
michael@0 | 682 | result.mFlags |= LAYER_RENDER_STATE_BUFFER_ROTATION; |
michael@0 | 683 | } |
michael@0 | 684 | result.SetOffset(GetOriginOffset()); |
michael@0 | 685 | return result; |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | #ifdef MOZ_DUMP_PAINTING |
michael@0 | 689 | TemporaryRef<gfx::DataSourceSurface> |
michael@0 | 690 | ContentHostTexture::GetAsSurface() |
michael@0 | 691 | { |
michael@0 | 692 | if (!mTextureHost) { |
michael@0 | 693 | return nullptr; |
michael@0 | 694 | } |
michael@0 | 695 | |
michael@0 | 696 | return mTextureHost->GetAsSurface(); |
michael@0 | 697 | } |
michael@0 | 698 | |
michael@0 | 699 | #endif |
michael@0 | 700 | |
michael@0 | 701 | |
michael@0 | 702 | } // namespace |
michael@0 | 703 | } // namespace |