|
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 } |