gfx/layers/client/ContentClient.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:3bc470666add
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/. */
5
6 #include "mozilla/layers/ContentClient.h"
7 #include "BasicLayers.h" // for BasicLayerManager
8 #include "gfxColor.h" // for gfxRGBA
9 #include "gfxContext.h" // for gfxContext, etc
10 #include "gfxPlatform.h" // for gfxPlatform
11 #include "gfxPrefs.h" // for gfxPrefs
12 #include "gfxPoint.h" // for gfxIntSize, gfxPoint
13 #include "gfxTeeSurface.h" // for gfxTeeSurface
14 #include "gfxUtils.h" // for gfxUtils
15 #include "ipc/ShadowLayers.h" // for ShadowLayerForwarder
16 #include "mozilla/ArrayUtils.h" // for ArrayLength
17 #include "mozilla/gfx/2D.h" // for DrawTarget, Factory
18 #include "mozilla/gfx/BasePoint.h" // for BasePoint
19 #include "mozilla/gfx/BaseSize.h" // for BaseSize
20 #include "mozilla/gfx/Rect.h" // for Rect
21 #include "mozilla/gfx/Types.h"
22 #include "mozilla/layers/LayerManagerComposite.h"
23 #include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
24 #include "mozilla/layers/LayersTypes.h"
25 #include "nsAutoPtr.h" // for nsRefPtr
26 #include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
27 #include "nsISupportsImpl.h" // for gfxContext::Release, etc
28 #include "nsIWidget.h" // for nsIWidget
29 #include "prenv.h" // for PR_GetEnv
30 #ifdef XP_WIN
31 #include "gfxWindowsPlatform.h"
32 #endif
33 #include "gfx2DGlue.h"
34
35 namespace mozilla {
36
37 using namespace gfx;
38
39 namespace layers {
40
41 static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags)
42 {
43 TextureFlags result = 0;
44
45 if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) {
46 result |= TEXTURE_COMPONENT_ALPHA;
47 }
48
49 if (aBufferFlags & RotatedContentBuffer::ALLOW_REPEAT) {
50 result |= TEXTURE_ALLOW_REPEAT;
51 }
52
53 return result;
54 }
55
56
57 /* static */ TemporaryRef<ContentClient>
58 ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
59 {
60 LayersBackend backend = aForwarder->GetCompositorBackendType();
61 if (backend != LayersBackend::LAYERS_OPENGL &&
62 backend != LayersBackend::LAYERS_D3D9 &&
63 backend != LayersBackend::LAYERS_D3D11 &&
64 backend != LayersBackend::LAYERS_BASIC) {
65 return nullptr;
66 }
67
68 bool useDoubleBuffering = false;
69
70 #ifdef XP_WIN
71 if (backend == LayersBackend::LAYERS_D3D11) {
72 useDoubleBuffering = !!gfxWindowsPlatform::GetPlatform()->GetD2DDevice();
73 } else
74 #endif
75 {
76 useDoubleBuffering = (LayerManagerComposite::SupportsDirectTexturing() &&
77 backend != LayersBackend::LAYERS_D3D9) ||
78 backend == LayersBackend::LAYERS_BASIC;
79 }
80
81 if (useDoubleBuffering || PR_GetEnv("MOZ_FORCE_DOUBLE_BUFFERING")) {
82 return new ContentClientDoubleBuffered(aForwarder);
83 }
84 #ifdef XP_MACOSX
85 if (backend == LayersBackend::LAYERS_OPENGL) {
86 return new ContentClientIncremental(aForwarder);
87 }
88 #endif
89 return new ContentClientSingleBuffered(aForwarder);
90 }
91
92 void
93 ContentClient::EndPaint()
94 {
95 // It is very important that this is called after any overridden EndPaint behaviour,
96 // because destroying textures is a three stage process:
97 // 1. We are done with the buffer and move it to ContentClient::mOldTextures,
98 // that happens in DestroyBuffers which is may be called indirectly from
99 // PaintThebes.
100 // 2. The content client calls RemoveTextureClient on the texture clients in
101 // mOldTextures and forgets them. They then become invalid. The compositable
102 // client keeps a record of IDs. This happens in EndPaint.
103 // 3. An IPC message is sent to destroy the corresponding texture host. That
104 // happens from OnTransaction.
105 // It is important that these steps happen in order.
106 OnTransaction();
107 }
108
109 // We pass a null pointer for the ContentClient Forwarder argument, which means
110 // this client will not have a ContentHost on the other side.
111 ContentClientBasic::ContentClientBasic()
112 : ContentClient(nullptr)
113 , RotatedContentBuffer(ContainsVisibleBounds)
114 {}
115
116 void
117 ContentClientBasic::CreateBuffer(ContentType aType,
118 const nsIntRect& aRect,
119 uint32_t aFlags,
120 RefPtr<gfx::DrawTarget>* aBlackDT,
121 RefPtr<gfx::DrawTarget>* aWhiteDT)
122 {
123 MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
124 gfxImageFormat format =
125 gfxPlatform::GetPlatform()->OptimalFormatForContent(aType);
126
127 *aBlackDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
128 IntSize(aRect.width, aRect.height),
129 ImageFormatToSurfaceFormat(format));
130 }
131
132 void
133 ContentClientRemoteBuffer::DestroyBuffers()
134 {
135 if (!mTextureClient) {
136 return;
137 }
138
139 mOldTextures.AppendElement(mTextureClient);
140 mTextureClient = nullptr;
141 if (mTextureClientOnWhite) {
142 mOldTextures.AppendElement(mTextureClientOnWhite);
143 mTextureClientOnWhite = nullptr;
144 }
145
146 DestroyFrontBuffer();
147 }
148
149 void
150 ContentClientRemoteBuffer::BeginPaint()
151 {
152 // XXX: So we might not have a TextureClient yet.. because it will
153 // only be created by CreateBuffer.. which will deliver a locked surface!.
154 if (mTextureClient) {
155 SetBufferProvider(mTextureClient);
156 }
157 if (mTextureClientOnWhite) {
158 SetBufferProviderOnWhite(mTextureClientOnWhite);
159 }
160 }
161
162 void
163 ContentClientRemoteBuffer::EndPaint()
164 {
165 // XXX: We might still not have a texture client if PaintThebes
166 // decided we didn't need one yet because the region to draw was empty.
167 SetBufferProvider(nullptr);
168 SetBufferProviderOnWhite(nullptr);
169 for (unsigned i = 0; i< mOldTextures.Length(); ++i) {
170 if (mOldTextures[i]->IsLocked()) {
171 mOldTextures[i]->Unlock();
172 }
173 }
174 mOldTextures.Clear();
175
176 if (mTextureClient && mTextureClient->IsLocked()) {
177 mTextureClient->Unlock();
178 }
179 if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
180 mTextureClientOnWhite->Unlock();
181 }
182 ContentClientRemote::EndPaint();
183 }
184
185 bool
186 ContentClientRemoteBuffer::CreateAndAllocateTextureClient(RefPtr<TextureClient>& aClient,
187 TextureFlags aFlags)
188 {
189 // gfx::BackendType::NONE means fallback to the content backend
190 aClient = CreateTextureClientForDrawing(mSurfaceFormat,
191 mTextureInfo.mTextureFlags | aFlags,
192 gfx::BackendType::NONE,
193 mSize);
194 if (!aClient) {
195 return false;
196 }
197
198 if (!aClient->AllocateForSurface(mSize, ALLOC_CLEAR_BUFFER)) {
199 aClient = CreateTextureClientForDrawing(mSurfaceFormat,
200 mTextureInfo.mTextureFlags | TEXTURE_ALLOC_FALLBACK | aFlags,
201 gfx::BackendType::NONE,
202 mSize);
203 if (!aClient) {
204 return false;
205 }
206 if (!aClient->AllocateForSurface(mSize, ALLOC_CLEAR_BUFFER)) {
207 NS_WARNING("Could not allocate texture client");
208 aClient = nullptr;
209 return false;
210 }
211 }
212
213 NS_WARN_IF_FALSE(aClient->IsValid(), "Created an invalid texture client");
214 return true;
215 }
216
217 void
218 ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat,
219 const nsIntRect& aRect,
220 uint32_t aFlags)
221 {
222 // If we hit this assertion, then it might be due to an empty transaction
223 // followed by a real transaction. Our buffers should be created (but not
224 // painted in the empty transaction) and then painted (but not created) in the
225 // real transaction. That is kind of fragile, and this assert will catch
226 // circumstances where we screw that up, e.g., by unnecessarily recreating our
227 // buffers.
228 NS_ABORT_IF_FALSE(!mIsNewBuffer,
229 "Bad! Did we create a buffer twice without painting?");
230
231 mIsNewBuffer = true;
232
233 DestroyBuffers();
234
235 mSurfaceFormat = aFormat;
236 mSize = gfx::IntSize(aRect.width, aRect.height);
237 mTextureInfo.mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags);
238
239 if (!CreateAndAllocateTextureClient(mTextureClient, TEXTURE_ON_BLACK) ||
240 !AddTextureClient(mTextureClient)) {
241 AbortTextureClientCreation();
242 return;
243 }
244
245 if (aFlags & BUFFER_COMPONENT_ALPHA) {
246 if (!CreateAndAllocateTextureClient(mTextureClientOnWhite, TEXTURE_ON_WHITE) ||
247 !AddTextureClient(mTextureClientOnWhite)) {
248 AbortTextureClientCreation();
249 return;
250 }
251 mTextureInfo.mTextureFlags |= TEXTURE_COMPONENT_ALPHA;
252 }
253
254 CreateFrontBuffer(aRect);
255 }
256
257 void
258 ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
259 const nsIntRect& aRect,
260 uint32_t aFlags,
261 RefPtr<gfx::DrawTarget>* aBlackDT,
262 RefPtr<gfx::DrawTarget>* aWhiteDT)
263 {
264 BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags);
265 if (!mTextureClient) {
266 return;
267 }
268
269 // We just created the textures and we are about to get their draw targets
270 // so we have to lock them here.
271 DebugOnly<bool> locked = mTextureClient->Lock(OPEN_READ_WRITE);
272 MOZ_ASSERT(locked, "Could not lock the TextureClient");
273
274 *aBlackDT = mTextureClient->GetAsDrawTarget();
275 if (aFlags & BUFFER_COMPONENT_ALPHA) {
276 locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE);
277 MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
278
279 *aWhiteDT = mTextureClientOnWhite->GetAsDrawTarget();
280 }
281 }
282
283 nsIntRegion
284 ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
285 const nsIntRegion& aVisibleRegion,
286 bool aDidSelfCopy)
287 {
288 nsIntRegion updatedRegion;
289 if (mIsNewBuffer || aDidSelfCopy) {
290 // A buffer reallocation clears both buffers. The front buffer has all the
291 // content by now, but the back buffer is still clear. Here, in effect, we
292 // are saying to copy all of the pixels of the front buffer to the back.
293 // Also when we self-copied in the buffer, the buffer space
294 // changes and some changed buffer content isn't reflected in the
295 // draw or invalidate region (on purpose!). When this happens, we
296 // need to read back the entire buffer too.
297 updatedRegion = aVisibleRegion;
298 mIsNewBuffer = false;
299 } else {
300 updatedRegion = aRegionToDraw;
301 }
302
303 NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()),
304 "Update outside of buffer rect!");
305 NS_ABORT_IF_FALSE(mTextureClient, "should have a back buffer by now");
306
307 return updatedRegion;
308 }
309
310 void
311 ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
312 const nsIntRegion& aVisibleRegion,
313 bool aDidSelfCopy)
314 {
315 nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw,
316 aVisibleRegion,
317 aDidSelfCopy);
318
319 MOZ_ASSERT(mTextureClient);
320 if (mTextureClientOnWhite) {
321 mForwarder->UseComponentAlphaTextures(this, mTextureClient,
322 mTextureClientOnWhite);
323 } else {
324 mForwarder->UseTexture(this, mTextureClient);
325 }
326 mForwarder->UpdateTextureRegion(this,
327 ThebesBufferData(BufferRect(),
328 BufferRotation()),
329 updatedRegion);
330 }
331
332 void
333 ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
334 {
335 MOZ_ASSERT(mTextureClient);
336 mFrontAndBackBufferDiffer = true;
337 }
338
339 void
340 ContentClientDoubleBuffered::CreateFrontBuffer(const nsIntRect& aBufferRect)
341 {
342 if (!CreateAndAllocateTextureClient(mFrontClient, TEXTURE_ON_BLACK) ||
343 !AddTextureClient(mFrontClient)) {
344 AbortTextureClientCreation();
345 return;
346 }
347 if (mTextureInfo.mTextureFlags & TEXTURE_COMPONENT_ALPHA) {
348 if (!CreateAndAllocateTextureClient(mFrontClientOnWhite, TEXTURE_ON_WHITE) ||
349 !AddTextureClient(mFrontClientOnWhite)) {
350 AbortTextureClientCreation();
351 return;
352 }
353 }
354
355 mFrontBufferRect = aBufferRect;
356 mFrontBufferRotation = nsIntPoint();
357 }
358
359 void
360 ContentClientDoubleBuffered::DestroyFrontBuffer()
361 {
362 MOZ_ASSERT(mFrontClient);
363
364 mOldTextures.AppendElement(mFrontClient);
365 mFrontClient = nullptr;
366 if (mFrontClientOnWhite) {
367 mOldTextures.AppendElement(mFrontClientOnWhite);
368 mFrontClientOnWhite = nullptr;
369 }
370 }
371
372 void
373 ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
374 {
375 mFrontUpdatedRegion = aFrontUpdatedRegion;
376
377 RefPtr<TextureClient> oldBack = mTextureClient;
378 mTextureClient = mFrontClient;
379 mFrontClient = oldBack;
380
381 oldBack = mTextureClientOnWhite;
382 mTextureClientOnWhite = mFrontClientOnWhite;
383 mFrontClientOnWhite = oldBack;
384
385 nsIntRect oldBufferRect = mBufferRect;
386 mBufferRect = mFrontBufferRect;
387 mFrontBufferRect = oldBufferRect;
388
389 nsIntPoint oldBufferRotation = mBufferRotation;
390 mBufferRotation = mFrontBufferRotation;
391 mFrontBufferRotation = oldBufferRotation;
392
393 MOZ_ASSERT(mFrontClient);
394
395 ContentClientRemoteBuffer::SwapBuffers(aFrontUpdatedRegion);
396 }
397
398 void
399 ContentClientDoubleBuffered::BeginPaint()
400 {
401 ContentClientRemoteBuffer::BeginPaint();
402
403 mIsNewBuffer = false;
404
405 if (!mFrontAndBackBufferDiffer) {
406 return;
407 }
408
409 if (mDidSelfCopy) {
410 // We can't easily draw our front buffer into us, since we're going to be
411 // copying stuff around anyway it's easiest if we just move our situation
412 // to non-rotated while we're at it. If this situation occurs we'll have
413 // hit a self-copy path in PaintThebes before as well anyway.
414 mBufferRect.MoveTo(mFrontBufferRect.TopLeft());
415 mBufferRotation = nsIntPoint();
416 return;
417 }
418 mBufferRect = mFrontBufferRect;
419 mBufferRotation = mFrontBufferRotation;
420 }
421
422 // Sync front/back buffers content
423 // After executing, the new back buffer has the same (interesting) pixels as
424 // the new front buffer, and mValidRegion et al. are correct wrt the new
425 // back buffer (i.e. as they were for the old back buffer)
426 void
427 ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
428 {
429 if (mTextureClient) {
430 DebugOnly<bool> locked = mTextureClient->Lock(OPEN_READ_WRITE);
431 MOZ_ASSERT(locked);
432 }
433 if (mTextureClientOnWhite) {
434 DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE);
435 MOZ_ASSERT(locked);
436 }
437
438 if (!mFrontAndBackBufferDiffer) {
439 MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?");
440 return;
441 }
442 MOZ_ASSERT(mFrontClient);
443
444 MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
445 this,
446 mFrontUpdatedRegion.GetBounds().x,
447 mFrontUpdatedRegion.GetBounds().y,
448 mFrontUpdatedRegion.GetBounds().width,
449 mFrontUpdatedRegion.GetBounds().height));
450
451 mFrontAndBackBufferDiffer = false;
452
453 nsIntRegion updateRegion = mFrontUpdatedRegion;
454 if (mDidSelfCopy) {
455 mDidSelfCopy = false;
456 updateRegion = mBufferRect;
457 }
458
459 // No point in sync'ing what we are going to draw over anyway. And if there is
460 // nothing to sync at all, there is nothing to do and we can go home early.
461 updateRegion.Sub(updateRegion, aRegionToDraw);
462 if (updateRegion.IsEmpty()) {
463 return;
464 }
465
466 // We need to ensure that we lock these two buffers in the same
467 // order as the compositor to prevent deadlocks.
468 if (!mFrontClient->Lock(OPEN_READ_ONLY)) {
469 return;
470 }
471 if (mFrontClientOnWhite &&
472 !mFrontClientOnWhite->Lock(OPEN_READ_ONLY)) {
473 mFrontClient->Unlock();
474 return;
475 }
476 {
477 // Restrict the DrawTargets and frontBuffer to a scope to make
478 // sure there is no more external references to the DrawTargets
479 // when we Unlock the TextureClients.
480 RefPtr<DrawTarget> dt = mFrontClient->GetAsDrawTarget();
481 RefPtr<DrawTarget> dtOnWhite = mFrontClientOnWhite
482 ? mFrontClientOnWhite->GetAsDrawTarget()
483 : nullptr;
484 RotatedBuffer frontBuffer(dt,
485 dtOnWhite,
486 mFrontBufferRect,
487 mFrontBufferRotation);
488 UpdateDestinationFrom(frontBuffer, updateRegion);
489 }
490
491 mFrontClient->Unlock();
492 if (mFrontClientOnWhite) {
493 mFrontClientOnWhite->Unlock();
494 }
495 }
496
497 void
498 ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
499 const nsIntRegion& aUpdateRegion)
500 {
501 DrawIterator iter;
502 while (DrawTarget* destDT =
503 BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) {
504 bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
505 if (isClippingCheap) {
506 gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
507 }
508
509 aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
510 if (isClippingCheap) {
511 destDT->PopClip();
512 }
513 // Flush the destination before the sources become inaccessible (Unlock).
514 destDT->Flush();
515 ReturnDrawTargetToBuffer(destDT);
516 }
517
518 if (aSource.HaveBufferOnWhite()) {
519 MOZ_ASSERT(HaveBufferOnWhite());
520 DrawIterator whiteIter;
521 while (DrawTarget* destDT =
522 BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) {
523 bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
524 if (isClippingCheap) {
525 gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
526 }
527
528 aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
529 if (isClippingCheap) {
530 destDT->PopClip();
531 }
532 // Flush the destination before the sources become inaccessible (Unlock).
533 destDT->Flush();
534 ReturnDrawTargetToBuffer(destDT);
535 }
536 }
537 }
538
539 void
540 ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
541 {
542 if (mTextureClient) {
543 DebugOnly<bool> locked = mTextureClient->Lock(OPEN_READ_WRITE);
544 MOZ_ASSERT(locked);
545 }
546 if (mTextureClientOnWhite) {
547 DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OPEN_READ_WRITE);
548 MOZ_ASSERT(locked);
549 }
550 }
551
552 static void
553 WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
554 {
555 if (*aRotationPoint < 0) {
556 *aRotationPoint += aSize;
557 } else if (*aRotationPoint >= aSize) {
558 *aRotationPoint -= aSize;
559 }
560 }
561
562 static void
563 FillSurface(DrawTarget* aDT, const nsIntRegion& aRegion,
564 const nsIntPoint& aOffset, const gfxRGBA& aColor)
565 {
566 nsIntRegionRectIterator iter(aRegion);
567 const nsIntRect* r;
568 while ((r = iter.Next()) != nullptr) {
569 aDT->FillRect(Rect(r->x - aOffset.x, r->y - aOffset.y,
570 r->width, r->height),
571 ColorPattern(ToColor(aColor)));
572 }
573 }
574
575 RotatedContentBuffer::PaintState
576 ContentClientIncremental::BeginPaintBuffer(ThebesLayer* aLayer,
577 uint32_t aFlags)
578 {
579 mTextureInfo.mDeprecatedTextureHostFlags = 0;
580 PaintState result;
581 // We need to disable rotation if we're going to be resampled when
582 // drawing, because we might sample across the rotation boundary.
583 bool canHaveRotation = !(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE);
584
585 nsIntRegion validRegion = aLayer->GetValidRegion();
586
587 bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
588 ContentType contentType =
589 canUseOpaqueSurface ? gfxContentType::COLOR :
590 gfxContentType::COLOR_ALPHA;
591
592 SurfaceMode mode;
593 nsIntRegion neededRegion;
594 bool canReuseBuffer;
595 nsIntRect destBufferRect;
596
597 while (true) {
598 mode = aLayer->GetSurfaceMode();
599 neededRegion = aLayer->GetVisibleRegion();
600 // If we're going to resample, we need a buffer that's in clamp mode.
601 canReuseBuffer = neededRegion.GetBounds().Size() <= mBufferRect.Size() &&
602 mHasBuffer &&
603 (!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) ||
604 !(mTextureInfo.mTextureFlags & TEXTURE_ALLOW_REPEAT));
605
606 if (canReuseBuffer) {
607 if (mBufferRect.Contains(neededRegion.GetBounds())) {
608 // We don't need to adjust mBufferRect.
609 destBufferRect = mBufferRect;
610 } else {
611 // The buffer's big enough but doesn't contain everything that's
612 // going to be visible. We'll move it.
613 destBufferRect = nsIntRect(neededRegion.GetBounds().TopLeft(), mBufferRect.Size());
614 }
615 } else {
616 destBufferRect = neededRegion.GetBounds();
617 }
618
619 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
620 if (!gfxPrefs::ComponentAlphaEnabled() ||
621 !aLayer->GetParent() ||
622 !aLayer->GetParent()->SupportsComponentAlphaChildren()) {
623 mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
624 } else {
625 contentType = gfxContentType::COLOR;
626 }
627 }
628
629 if ((aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) &&
630 (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
631 neededRegion.GetNumRects() > 1)) {
632 // The area we add to neededRegion might not be painted opaquely
633 if (mode == SurfaceMode::SURFACE_OPAQUE) {
634 contentType = gfxContentType::COLOR_ALPHA;
635 mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
636 }
637 // For component alpha layers, we leave contentType as gfxContentType::COLOR.
638
639 // We need to validate the entire buffer, to make sure that only valid
640 // pixels are sampled
641 neededRegion = destBufferRect;
642 }
643
644 if (mHasBuffer &&
645 (mContentType != contentType ||
646 (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) != mHasBufferOnWhite)) {
647 // We're effectively clearing the valid region, so we need to draw
648 // the entire needed region now.
649 result.mRegionToInvalidate = aLayer->GetValidRegion();
650 validRegion.SetEmpty();
651 mHasBuffer = false;
652 mHasBufferOnWhite = false;
653 mBufferRect.SetRect(0, 0, 0, 0);
654 mBufferRotation.MoveTo(0, 0);
655 // Restart decision process with the cleared buffer. We can only go
656 // around the loop one more iteration, since mTexImage is null now.
657 continue;
658 }
659
660 break;
661 }
662
663 result.mRegionToDraw.Sub(neededRegion, validRegion);
664 if (result.mRegionToDraw.IsEmpty())
665 return result;
666
667 if (destBufferRect.width > mForwarder->GetMaxTextureSize() ||
668 destBufferRect.height > mForwarder->GetMaxTextureSize()) {
669 return result;
670 }
671
672 // BlitTextureImage depends on the FBO texture target being
673 // TEXTURE_2D. This isn't the case on some older X1600-era Radeons.
674 if (!mForwarder->SupportsTextureBlitting() ||
675 !mForwarder->SupportsPartialUploads()) {
676 result.mRegionToDraw = neededRegion;
677 validRegion.SetEmpty();
678 mHasBuffer = false;
679 mHasBufferOnWhite = false;
680 mBufferRect.SetRect(0, 0, 0, 0);
681 mBufferRotation.MoveTo(0, 0);
682 canReuseBuffer = false;
683 }
684
685 nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
686 bool createdBuffer = false;
687
688 uint32_t bufferFlags = canHaveRotation ? TEXTURE_ALLOW_REPEAT : 0;
689 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
690 bufferFlags |= TEXTURE_COMPONENT_ALPHA;
691 }
692 if (canReuseBuffer) {
693 nsIntRect keepArea;
694 if (keepArea.IntersectRect(destBufferRect, mBufferRect)) {
695 // Set mBufferRotation so that the pixels currently in mBuffer
696 // will still be rendered in the right place when mBufferRect
697 // changes to destBufferRect.
698 nsIntPoint newRotation = mBufferRotation +
699 (destBufferRect.TopLeft() - mBufferRect.TopLeft());
700 WrapRotationAxis(&newRotation.x, mBufferRect.width);
701 WrapRotationAxis(&newRotation.y, mBufferRect.height);
702 NS_ASSERTION(nsIntRect(nsIntPoint(0,0), mBufferRect.Size()).Contains(newRotation),
703 "newRotation out of bounds");
704 int32_t xBoundary = destBufferRect.XMost() - newRotation.x;
705 int32_t yBoundary = destBufferRect.YMost() - newRotation.y;
706 if ((drawBounds.x < xBoundary && xBoundary < drawBounds.XMost()) ||
707 (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost()) ||
708 (newRotation != nsIntPoint(0,0) && !canHaveRotation)) {
709 // The stuff we need to redraw will wrap around an edge of the
710 // buffer, so we will need to do a self-copy
711 // If mBufferRotation == nsIntPoint(0,0) we could do a real
712 // self-copy but we're not going to do that in GL yet.
713 // We can't do a real self-copy because the buffer is rotated.
714 // So allocate a new buffer for the destination.
715 destBufferRect = neededRegion.GetBounds();
716 createdBuffer = true;
717 } else {
718 mBufferRect = destBufferRect;
719 mBufferRotation = newRotation;
720 }
721 } else {
722 // No pixels are going to be kept. The whole visible region
723 // will be redrawn, so we don't need to copy anything, so we don't
724 // set destBuffer.
725 mBufferRect = destBufferRect;
726 mBufferRotation = nsIntPoint(0,0);
727 }
728 } else {
729 // The buffer's not big enough, so allocate a new one
730 createdBuffer = true;
731 }
732 NS_ASSERTION(!(aFlags & RotatedContentBuffer::PAINT_WILL_RESAMPLE) ||
733 destBufferRect == neededRegion.GetBounds(),
734 "If we're resampling, we need to validate the entire buffer");
735
736 if (!createdBuffer && !mHasBuffer) {
737 return result;
738 }
739
740 if (createdBuffer) {
741 if (mHasBuffer &&
742 (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || mHasBufferOnWhite)) {
743 mTextureInfo.mDeprecatedTextureHostFlags = TEXTURE_HOST_COPY_PREVIOUS;
744 }
745
746 mHasBuffer = true;
747 if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
748 mHasBufferOnWhite = true;
749 }
750 mBufferRect = destBufferRect;
751 mBufferRotation = nsIntPoint(0,0);
752 NotifyBufferCreated(contentType, bufferFlags);
753 }
754
755 NS_ASSERTION(canHaveRotation || mBufferRotation == nsIntPoint(0,0),
756 "Rotation disabled, but we have nonzero rotation?");
757
758 nsIntRegion invalidate;
759 invalidate.Sub(aLayer->GetValidRegion(), destBufferRect);
760 result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
761
762 // If we do partial updates, we have to clip drawing to the regionToDraw.
763 // If we don't clip, background images will be fillrect'd to the region correctly,
764 // while text or lines will paint outside of the regionToDraw. This becomes apparent
765 // with concave regions. Right now the scrollbars invalidate a narrow strip of the bar
766 // although they never cover it. This leads to two draw rects, the narow strip and the actually
767 // newly exposed area. It would be wise to fix this glitch in any way to have simpler
768 // clip and draw regions.
769 result.mClip = DrawRegionClip::DRAW;
770 result.mMode = mode;
771
772 return result;
773 }
774
775 DrawTarget*
776 ContentClientIncremental::BorrowDrawTargetForPainting(const PaintState& aPaintState,
777 RotatedContentBuffer::DrawIterator* aIter)
778 {
779 if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
780 return nullptr;
781 }
782
783 if (aIter) {
784 if (aIter->mCount++ > 0) {
785 return nullptr;
786 }
787 aIter->mDrawRegion = aPaintState.mRegionToDraw;
788 }
789
790 DrawTarget* result = nullptr;
791
792 nsIntRect drawBounds = aPaintState.mRegionToDraw.GetBounds();
793 MOZ_ASSERT(!mLoanedDrawTarget);
794
795 // BeginUpdate is allowed to modify the given region,
796 // if it wants more to be repainted than we request.
797 if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
798 nsIntRegion drawRegionCopy = aPaintState.mRegionToDraw;
799 RefPtr<DrawTarget> onBlack = GetUpdateSurface(BUFFER_BLACK, drawRegionCopy);
800 RefPtr<DrawTarget> onWhite = GetUpdateSurface(BUFFER_WHITE, aPaintState.mRegionToDraw);
801 if (onBlack && onWhite) {
802 NS_ASSERTION(aPaintState.mRegionToDraw == drawRegionCopy,
803 "BeginUpdate should always modify the draw region in the same way!");
804 FillSurface(onBlack, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0));
805 FillSurface(onWhite, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0));
806 mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlack, onWhite);
807 } else {
808 mLoanedDrawTarget = nullptr;
809 }
810 } else {
811 mLoanedDrawTarget = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw);
812 }
813 if (!mLoanedDrawTarget) {
814 NS_WARNING("unable to get context for update");
815 return nullptr;
816 }
817
818 result = mLoanedDrawTarget;
819 mLoanedTransform = mLoanedDrawTarget->GetTransform();
820 mLoanedTransform.Translate(-drawBounds.x, -drawBounds.y);
821 result->SetTransform(mLoanedTransform);
822 mLoanedTransform.Translate(drawBounds.x, drawBounds.y);
823
824 if (mContentType == gfxContentType::COLOR_ALPHA) {
825 gfxUtils::ClipToRegion(result, aPaintState.mRegionToDraw);
826 nsIntRect bounds = aPaintState.mRegionToDraw.GetBounds();
827 result->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height));
828 }
829
830 return result;
831 }
832
833 void
834 ContentClientIncremental::Updated(const nsIntRegion& aRegionToDraw,
835 const nsIntRegion& aVisibleRegion,
836 bool aDidSelfCopy)
837 {
838 if (IsSurfaceDescriptorValid(mUpdateDescriptor)) {
839 mForwarder->UpdateTextureIncremental(this,
840 TextureFront,
841 mUpdateDescriptor,
842 aRegionToDraw,
843 mBufferRect,
844 mBufferRotation);
845 mUpdateDescriptor = SurfaceDescriptor();
846 }
847 if (IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite)) {
848 mForwarder->UpdateTextureIncremental(this,
849 TextureOnWhiteFront,
850 mUpdateDescriptorOnWhite,
851 aRegionToDraw,
852 mBufferRect,
853 mBufferRotation);
854 mUpdateDescriptorOnWhite = SurfaceDescriptor();
855 }
856
857 }
858
859 TemporaryRef<DrawTarget>
860 ContentClientIncremental::GetUpdateSurface(BufferType aType,
861 const nsIntRegion& aUpdateRegion)
862 {
863 nsIntRect rgnSize = aUpdateRegion.GetBounds();
864 if (!mBufferRect.Contains(rgnSize)) {
865 NS_ERROR("update outside of image");
866 return nullptr;
867 }
868 SurfaceDescriptor desc;
869 if (!mForwarder->AllocSurfaceDescriptor(rgnSize.Size().ToIntSize(),
870 mContentType,
871 &desc)) {
872 NS_WARNING("creating SurfaceDescriptor failed!");
873 Clear();
874 return nullptr;
875 }
876
877 if (aType == BUFFER_BLACK) {
878 MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptor));
879 mUpdateDescriptor = desc;
880 } else {
881 MOZ_ASSERT(!IsSurfaceDescriptorValid(mUpdateDescriptorOnWhite));
882 MOZ_ASSERT(aType == BUFFER_WHITE);
883 mUpdateDescriptorOnWhite = desc;
884 }
885
886 return GetDrawTargetForDescriptor(desc, gfx::BackendType::COREGRAPHICS);
887 }
888
889 }
890 }

mercurial