Line data Source code
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 "BasicCompositor.h"
7 : #include "BasicLayersImpl.h" // for FillRectWithMask
8 : #include "TextureHostBasic.h"
9 : #include "mozilla/layers/Effects.h"
10 : #include "nsIWidget.h"
11 : #include "gfx2DGlue.h"
12 : #include "mozilla/gfx/2D.h"
13 : #include "mozilla/gfx/gfxVars.h"
14 : #include "mozilla/gfx/Helpers.h"
15 : #include "mozilla/gfx/Tools.h"
16 : #include "mozilla/gfx/ssse3-scaler.h"
17 : #include "mozilla/layers/ImageDataSerializer.h"
18 : #include "mozilla/SSE.h"
19 : #include "gfxUtils.h"
20 : #include "YCbCrUtils.h"
21 : #include <algorithm>
22 : #include "ImageContainer.h"
23 : #include "gfxPrefs.h"
24 :
25 : namespace mozilla {
26 : using namespace mozilla::gfx;
27 :
28 : namespace layers {
29 :
30 18 : class DataTextureSourceBasic : public DataTextureSource
31 : , public TextureSourceBasic
32 : {
33 : public:
34 0 : virtual const char* Name() const override { return "DataTextureSourceBasic"; }
35 :
36 9 : explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
37 9 : : mSurface(aSurface)
38 9 : , mWrappingExistingData(!!aSurface)
39 9 : {}
40 :
41 0 : virtual DataTextureSource* AsDataTextureSource() override
42 : {
43 : // If the texture wraps someone else's memory we'd rather not use it as
44 : // a DataTextureSource per say (that is call Update on it).
45 0 : return mWrappingExistingData ? nullptr : this;
46 : }
47 :
48 56 : virtual TextureSourceBasic* AsSourceBasic() override { return this; }
49 :
50 56 : virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
51 :
52 56 : SurfaceFormat GetFormat() const override
53 : {
54 56 : return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
55 : }
56 :
57 56 : virtual IntSize GetSize() const override
58 : {
59 56 : return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
60 : }
61 :
62 0 : virtual bool Update(gfx::DataSourceSurface* aSurface,
63 : nsIntRegion* aDestRegion = nullptr,
64 : gfx::IntPoint* aSrcOffset = nullptr) override
65 : {
66 0 : MOZ_ASSERT(!mWrappingExistingData);
67 0 : if (mWrappingExistingData) {
68 0 : return false;
69 : }
70 0 : mSurface = aSurface;
71 0 : return true;
72 : }
73 :
74 6 : virtual void DeallocateDeviceData() override
75 : {
76 6 : mSurface = nullptr;
77 6 : SetUpdateSerial(0);
78 6 : }
79 :
80 : public:
81 : RefPtr<gfx::DataSourceSurface> mSurface;
82 : bool mWrappingExistingData;
83 : };
84 :
85 : /**
86 : * WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost to defer
87 : * yuv->rgb conversion. The conversion happens when GetSurface is called.
88 : */
89 0 : class WrappingTextureSourceYCbCrBasic : public DataTextureSource
90 : , public TextureSourceBasic
91 : {
92 : public:
93 0 : virtual const char* Name() const override { return "WrappingTextureSourceYCbCrBasic"; }
94 :
95 0 : explicit WrappingTextureSourceYCbCrBasic(BufferTextureHost* aTexture)
96 0 : : mTexture(aTexture)
97 0 : , mSize(aTexture->GetSize())
98 0 : , mNeedsUpdate(true)
99 : {
100 0 : mFromYCBCR = true;
101 0 : }
102 :
103 0 : virtual DataTextureSource* AsDataTextureSource() override
104 : {
105 0 : return this;
106 : }
107 :
108 0 : virtual TextureSourceBasic* AsSourceBasic() override { return this; }
109 :
110 0 : virtual WrappingTextureSourceYCbCrBasic* AsWrappingTextureSourceYCbCrBasic() override { return this; }
111 :
112 0 : virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override
113 : {
114 0 : if (mSurface && !mNeedsUpdate) {
115 0 : return mSurface;
116 : }
117 0 : MOZ_ASSERT(mTexture);
118 0 : if (!mTexture) {
119 0 : return nullptr;
120 : }
121 :
122 0 : if (!mSurface) {
123 0 : mSurface = Factory::CreateDataSourceSurface(mSize, gfx::SurfaceFormat::B8G8R8X8);
124 : }
125 0 : if (!mSurface) {
126 0 : return nullptr;
127 : }
128 0 : MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
129 0 : MOZ_ASSERT(mTexture->GetSize() == mSize);
130 :
131 : mSurface =
132 0 : ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
133 0 : mTexture->GetBuffer(),
134 0 : mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
135 0 : mSurface);
136 0 : mNeedsUpdate = false;
137 0 : return mSurface;
138 : }
139 :
140 0 : SurfaceFormat GetFormat() const override
141 : {
142 0 : return gfx::SurfaceFormat::B8G8R8X8;
143 : }
144 :
145 0 : virtual IntSize GetSize() const override
146 : {
147 0 : return mSize;
148 : }
149 :
150 0 : virtual bool Update(gfx::DataSourceSurface* aSurface,
151 : nsIntRegion* aDestRegion = nullptr,
152 : gfx::IntPoint* aSrcOffset = nullptr) override
153 : {
154 0 : return false;
155 : }
156 :
157 0 : virtual void DeallocateDeviceData() override
158 : {
159 0 : mTexture = nullptr;
160 0 : mSurface = nullptr;
161 0 : SetUpdateSerial(0);
162 0 : }
163 :
164 0 : virtual void Unbind() override
165 : {
166 0 : mNeedsUpdate = true;
167 0 : }
168 :
169 0 : void SetBufferTextureHost(BufferTextureHost* aTexture) override
170 : {
171 0 : mTexture = aTexture;
172 0 : mNeedsUpdate = true;
173 0 : }
174 :
175 0 : void ConvertAndScale(const SurfaceFormat& aDestFormat,
176 : const IntSize& aDestSize,
177 : unsigned char* aDestBuffer,
178 : int32_t aStride)
179 : {
180 0 : MOZ_ASSERT(mTexture);
181 0 : if (!mTexture) {
182 0 : return;
183 : }
184 0 : MOZ_ASSERT(mTexture->GetBufferDescriptor().type() == BufferDescriptor::TYCbCrDescriptor);
185 0 : MOZ_ASSERT(mTexture->GetSize() == mSize);
186 0 : ImageDataSerializer::ConvertAndScaleFromYCbCrDescriptor(
187 0 : mTexture->GetBuffer(),
188 0 : mTexture->GetBufferDescriptor().get_YCbCrDescriptor(),
189 : aDestFormat,
190 : aDestSize,
191 : aDestBuffer,
192 0 : aStride);
193 : }
194 : public:
195 : BufferTextureHost* mTexture;
196 : const gfx::IntSize mSize;
197 : RefPtr<gfx::DataSourceSurface> mSurface;
198 : bool mNeedsUpdate;
199 : };
200 :
201 1 : BasicCompositor::BasicCompositor(CompositorBridgeParent* aParent, widget::CompositorWidget* aWidget)
202 : : Compositor(aWidget, aParent)
203 1 : , mIsPendingEndRemoteDrawing(false)
204 : {
205 1 : MOZ_COUNT_CTOR(BasicCompositor);
206 :
207 1 : mMaxTextureSize = Factory::GetMaxSurfaceSize(gfxVars::ContentBackend());
208 1 : }
209 :
210 0 : BasicCompositor::~BasicCompositor()
211 : {
212 0 : MOZ_COUNT_DTOR(BasicCompositor);
213 0 : }
214 :
215 : bool
216 1 : BasicCompositor::Initialize(nsCString* const out_failureReason)
217 : {
218 1 : return mWidget ? mWidget->InitCompositor(this) : false;
219 : };
220 :
221 : int32_t
222 1 : BasicCompositor::GetMaxTextureSize() const
223 : {
224 1 : return mMaxTextureSize;
225 : }
226 :
227 : void
228 27 : BasicCompositingRenderTarget::BindRenderTarget()
229 : {
230 27 : if (mClearOnBind) {
231 0 : mDrawTarget->ClearRect(Rect(0, 0, mSize.width, mSize.height));
232 0 : mClearOnBind = false;
233 : }
234 27 : }
235 :
236 0 : void BasicCompositor::DetachWidget()
237 : {
238 0 : if (mWidget) {
239 0 : if (mIsPendingEndRemoteDrawing) {
240 : // Force to end previous remote drawing.
241 0 : TryToEndRemoteDrawing(/* aForceToEnd */ true);
242 0 : MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
243 : }
244 0 : mWidget->CleanupRemoteDrawing();
245 : }
246 0 : Compositor::DetachWidget();
247 0 : }
248 :
249 : TextureFactoryIdentifier
250 1 : BasicCompositor::GetTextureFactoryIdentifier()
251 : {
252 : TextureFactoryIdentifier ident(LayersBackend::LAYERS_BASIC,
253 : XRE_GetProcessType(),
254 1 : GetMaxTextureSize());
255 1 : return ident;
256 : }
257 :
258 : already_AddRefed<CompositingRenderTarget>
259 0 : BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit)
260 : {
261 0 : MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
262 :
263 0 : if (aRect.width * aRect.height == 0) {
264 0 : return nullptr;
265 : }
266 :
267 0 : RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
268 :
269 0 : if (!target) {
270 0 : return nullptr;
271 : }
272 :
273 0 : RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect);
274 :
275 0 : return rt.forget();
276 : }
277 :
278 : already_AddRefed<CompositingRenderTarget>
279 0 : BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect,
280 : const CompositingRenderTarget *aSource,
281 : const IntPoint &aSourcePoint)
282 : {
283 0 : MOZ_CRASH("GFX: Shouldn't be called!");
284 : return nullptr;
285 : }
286 :
287 : already_AddRefed<CompositingRenderTarget>
288 27 : BasicCompositor::CreateRenderTargetForWindow(const LayoutDeviceIntRect& aRect, const LayoutDeviceIntRect& aClearRect, BufferMode aBufferMode)
289 : {
290 27 : MOZ_ASSERT(mDrawTarget);
291 27 : MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
292 :
293 27 : if (aRect.width * aRect.height == 0) {
294 0 : return nullptr;
295 : }
296 :
297 54 : RefPtr<BasicCompositingRenderTarget> rt;
298 27 : IntRect rect = aRect.ToUnknownRect();
299 :
300 27 : if (aBufferMode != BufferMode::BUFFER_NONE) {
301 0 : RefPtr<DrawTarget> target = mWidget->GetBackBufferDrawTarget(mDrawTarget, aRect, aClearRect);
302 0 : if (!target) {
303 0 : return nullptr;
304 : }
305 0 : MOZ_ASSERT(target != mDrawTarget);
306 0 : rt = new BasicCompositingRenderTarget(target, rect);
307 : } else {
308 27 : IntRect windowRect = rect;
309 : // Adjust bounds rect to account for new origin at (0, 0).
310 27 : if (windowRect.Size() != mDrawTarget->GetSize()) {
311 0 : windowRect.ExpandToEnclose(IntPoint(0, 0));
312 : }
313 54 : rt = new BasicCompositingRenderTarget(mDrawTarget, windowRect);
314 27 : if (!aClearRect.IsEmpty()) {
315 1 : IntRect clearRect = aRect.ToUnknownRect();
316 1 : mDrawTarget->ClearRect(Rect(clearRect - rt->GetOrigin()));
317 : }
318 : }
319 :
320 27 : return rt.forget();
321 : }
322 :
323 : already_AddRefed<DataTextureSource>
324 0 : BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
325 : {
326 0 : RefPtr<DataTextureSourceBasic> result = new DataTextureSourceBasic(nullptr);
327 0 : if (aFlags & TextureFlags::RGB_FROM_YCBCR) {
328 0 : result->mFromYCBCR = true;
329 : }
330 0 : return result.forget();
331 : }
332 :
333 : already_AddRefed<DataTextureSource>
334 9 : BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
335 : {
336 18 : RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
337 18 : return result.forget();
338 : }
339 :
340 : already_AddRefed<DataTextureSource>
341 0 : BasicCompositor::CreateDataTextureSourceAroundYCbCr(TextureHost* aTexture)
342 : {
343 0 : BufferTextureHost* bufferTexture = aTexture->AsBufferTextureHost();
344 0 : MOZ_ASSERT(bufferTexture);
345 :
346 0 : if (!bufferTexture) {
347 0 : return nullptr;
348 : }
349 0 : RefPtr<DataTextureSource> result = new WrappingTextureSourceYCbCrBasic(bufferTexture);
350 0 : return result.forget();
351 : }
352 :
353 : bool
354 0 : BasicCompositor::SupportsEffect(EffectTypes aEffect)
355 : {
356 0 : return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
357 : }
358 :
359 : bool
360 88 : BasicCompositor::SupportsLayerGeometry() const
361 : {
362 88 : return gfxPrefs::BasicLayerGeometry();
363 : }
364 :
365 : static RefPtr<gfx::Path>
366 0 : BuildPathFromPolygon(const RefPtr<DrawTarget>& aDT,
367 : const gfx::Polygon& aPolygon)
368 : {
369 0 : MOZ_ASSERT(!aPolygon.IsEmpty());
370 :
371 0 : RefPtr<PathBuilder> pathBuilder = aDT->CreatePathBuilder();
372 0 : const nsTArray<Point4D>& points = aPolygon.GetPoints();
373 :
374 0 : pathBuilder->MoveTo(points[0].As2DPoint());
375 :
376 0 : for (size_t i = 1; i < points.Length(); ++i) {
377 0 : pathBuilder->LineTo(points[i].As2DPoint());
378 : }
379 :
380 0 : pathBuilder->Close();
381 0 : return pathBuilder->Finish();
382 : }
383 :
384 : static void
385 5 : DrawSurface(gfx::DrawTarget* aDest,
386 : const gfx::Rect& aDestRect,
387 : const gfx::Rect& /* aClipRect */,
388 : const gfx::Color& aColor,
389 : const gfx::DrawOptions& aOptions,
390 : gfx::SourceSurface* aMask,
391 : const gfx::Matrix* aMaskTransform)
392 : {
393 : FillRectWithMask(aDest, aDestRect, aColor,
394 5 : aOptions, aMask, aMaskTransform);
395 5 : }
396 :
397 : static void
398 0 : DrawSurface(gfx::DrawTarget* aDest,
399 : const gfx::Polygon& aPolygon,
400 : const gfx::Rect& aClipRect,
401 : const gfx::Color& aColor,
402 : const gfx::DrawOptions& aOptions,
403 : gfx::SourceSurface* aMask,
404 : const gfx::Matrix* aMaskTransform)
405 : {
406 0 : RefPtr<Path> path = BuildPathFromPolygon(aDest, aPolygon);
407 : FillPathWithMask(aDest, path, aClipRect, aColor,
408 0 : aOptions, aMask, aMaskTransform);
409 0 : }
410 :
411 : static void
412 56 : DrawTextureSurface(gfx::DrawTarget* aDest,
413 : const gfx::Rect& aDestRect,
414 : const gfx::Rect& /* aClipRect */,
415 : gfx::SourceSurface* aSource,
416 : gfx::SamplingFilter aSamplingFilter,
417 : const gfx::DrawOptions& aOptions,
418 : ExtendMode aExtendMode,
419 : gfx::SourceSurface* aMask,
420 : const gfx::Matrix* aMaskTransform,
421 : const Matrix* aSurfaceTransform)
422 : {
423 : FillRectWithMask(aDest, aDestRect, aSource, aSamplingFilter, aOptions,
424 56 : aExtendMode, aMask, aMaskTransform, aSurfaceTransform);
425 56 : }
426 :
427 : static void
428 0 : DrawTextureSurface(gfx::DrawTarget* aDest,
429 : const gfx::Polygon& aPolygon,
430 : const gfx::Rect& aClipRect,
431 : gfx::SourceSurface* aSource,
432 : gfx::SamplingFilter aSamplingFilter,
433 : const gfx::DrawOptions& aOptions,
434 : ExtendMode aExtendMode,
435 : gfx::SourceSurface* aMask,
436 : const gfx::Matrix* aMaskTransform,
437 : const Matrix* aSurfaceTransform)
438 : {
439 0 : RefPtr<Path> path = BuildPathFromPolygon(aDest, aPolygon);
440 : FillPathWithMask(aDest, path, aClipRect, aSource, aSamplingFilter, aOptions,
441 0 : aExtendMode, aMask, aMaskTransform, aSurfaceTransform);
442 0 : }
443 :
444 : template<typename Geometry>
445 : static void
446 56 : DrawSurfaceWithTextureCoords(gfx::DrawTarget* aDest,
447 : const Geometry& aGeometry,
448 : const gfx::Rect& aDestRect,
449 : gfx::SourceSurface* aSource,
450 : const gfx::Rect& aTextureCoords,
451 : gfx::SamplingFilter aSamplingFilter,
452 : const gfx::DrawOptions& aOptions,
453 : gfx::SourceSurface* aMask,
454 : const gfx::Matrix* aMaskTransform)
455 : {
456 56 : if (!aSource) {
457 0 : gfxWarning() << "DrawSurfaceWithTextureCoords problem " << gfx::hexa(aSource) << " and " << gfx::hexa(aMask);
458 0 : return;
459 : }
460 :
461 : // Convert aTextureCoords into aSource's coordinate space
462 112 : gfxRect sourceRect(aTextureCoords.x * aSource->GetSize().width,
463 112 : aTextureCoords.y * aSource->GetSize().height,
464 112 : aTextureCoords.width * aSource->GetSize().width,
465 280 : aTextureCoords.height * aSource->GetSize().height);
466 :
467 : // Floating point error can accumulate above and we know our visible region
468 : // is integer-aligned, so round it out.
469 56 : sourceRect.Round();
470 :
471 : // Compute a transform that maps sourceRect to aDestRect.
472 : Matrix matrix =
473 : gfxUtils::TransformRectToRect(sourceRect,
474 112 : gfx::IntPoint::Truncate(aDestRect.x, aDestRect.y),
475 112 : gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.y),
476 168 : gfx::IntPoint::Truncate(aDestRect.XMost(), aDestRect.YMost()));
477 :
478 : // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
479 56 : gfx::Rect unitRect(0, 0, 1, 1);
480 56 : ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT;
481 :
482 56 : DrawTextureSurface(aDest, aGeometry, aDestRect, aSource, aSamplingFilter,
483 : aOptions, mode, aMask, aMaskTransform, &matrix);
484 : }
485 :
486 : static void
487 61 : SetupMask(const EffectChain& aEffectChain,
488 : DrawTarget* aDest,
489 : const IntPoint& aOffset,
490 : RefPtr<SourceSurface>& aMaskSurface,
491 : Matrix& aMaskTransform)
492 : {
493 61 : if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
494 0 : EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
495 0 : aMaskSurface = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(aDest);
496 0 : if (!aMaskSurface) {
497 0 : gfxWarning() << "Invalid sourceMask effect";
498 : }
499 0 : MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!");
500 0 : aMaskTransform = effectMask->mMaskTransform.As2D();
501 0 : aMaskTransform.PostTranslate(-aOffset.x, -aOffset.y);
502 : }
503 61 : }
504 :
505 : static bool
506 0 : AttemptVideoScale(TextureSourceBasic* aSource, const SourceSurface* aSourceMask,
507 : gfx::Float aOpacity, CompositionOp aBlendMode,
508 : const TexturedEffect* aTexturedEffect,
509 : const Matrix& aNewTransform, const gfx::Rect& aRect,
510 : const gfx::Rect& aClipRect,
511 : DrawTarget* aDest, const DrawTarget* aBuffer)
512 : {
513 : #ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
514 0 : if (!mozilla::supports_ssse3())
515 0 : return false;
516 0 : if (aNewTransform.IsTranslation()) // unscaled painting should take the regular path
517 0 : return false;
518 0 : if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
519 0 : return false;
520 0 : if (aSourceMask || aOpacity != 1.0f)
521 0 : return false;
522 0 : if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
523 0 : return false;
524 :
525 0 : IntRect dstRect;
526 : // the compiler should know a lot about aNewTransform at this point
527 : // maybe it can do some sophisticated optimization of the following
528 0 : if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
529 0 : return false;
530 :
531 0 : IntRect clipRect;
532 0 : if (!aClipRect.ToIntRect(&clipRect))
533 0 : return false;
534 :
535 0 : if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
536 0 : return false;
537 0 : if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
538 0 : return false;
539 :
540 : uint8_t* dstData;
541 0 : IntSize dstSize;
542 : int32_t dstStride;
543 : SurfaceFormat dstFormat;
544 0 : if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
545 : // If we're not painting to aBuffer the clip will
546 : // be applied later
547 0 : IntRect fillRect = dstRect;
548 0 : if (aDest == aBuffer) {
549 : // we need to clip fillRect because LockBits ignores the clip on the aDest
550 0 : fillRect = fillRect.Intersect(clipRect);
551 : }
552 :
553 0 : fillRect = fillRect.Intersect(IntRect(IntPoint(0, 0), aDest->GetSize()));
554 0 : IntPoint offset = fillRect.TopLeft() - dstRect.TopLeft();
555 :
556 0 : RefPtr<DataSourceSurface> srcSource = aSource->GetSurface(aDest)->GetDataSurface();
557 0 : DataSourceSurface::ScopedMap mapSrc(srcSource, DataSourceSurface::READ);
558 :
559 0 : ssse3_scale_data((uint32_t*)mapSrc.GetData(), srcSource->GetSize().width, srcSource->GetSize().height,
560 0 : mapSrc.GetStride()/4,
561 0 : ((uint32_t*)dstData) + fillRect.x + (dstStride / 4) * fillRect.y, dstRect.width, dstRect.height,
562 : dstStride / 4,
563 : offset.x, offset.y,
564 0 : fillRect.width, fillRect.height);
565 :
566 0 : aDest->ReleaseBits(dstData);
567 0 : return true;
568 : } else
569 : #endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
570 0 : return false;
571 : }
572 :
573 : static bool
574 0 : AttemptVideoConvertAndScale(TextureSource* aSource, const SourceSurface* aSourceMask,
575 : gfx::Float aOpacity, CompositionOp aBlendMode,
576 : const TexturedEffect* aTexturedEffect,
577 : const Matrix& aNewTransform, const gfx::Rect& aRect,
578 : const gfx::Rect& aClipRect,
579 : DrawTarget* aDest, const DrawTarget* aBuffer)
580 : {
581 : #if defined(XP_WIN) && defined(_M_X64)
582 : // libyuv does not support SIMD scaling on win 64bit. See Bug 1295927.
583 : return false;
584 : #endif
585 :
586 0 : WrappingTextureSourceYCbCrBasic* wrappingSource = aSource->AsWrappingTextureSourceYCbCrBasic();
587 0 : if (!wrappingSource)
588 0 : return false;
589 : #ifdef MOZILLA_SSE_HAVE_CPUID_DETECTION
590 0 : if (!mozilla::supports_ssse3()) // libyuv requests SSSE3 for fast YUV conversion.
591 0 : return false;
592 0 : if (aNewTransform.HasNonAxisAlignedTransform() || aNewTransform.HasNegativeScaling())
593 0 : return false;
594 0 : if (aSourceMask || aOpacity != 1.0f)
595 0 : return false;
596 0 : if (aBlendMode != CompositionOp::OP_OVER && aBlendMode != CompositionOp::OP_SOURCE)
597 0 : return false;
598 :
599 0 : IntRect dstRect;
600 : // the compiler should know a lot about aNewTransform at this point
601 : // maybe it can do some sophisticated optimization of the following
602 0 : if (!aNewTransform.TransformBounds(aRect).ToIntRect(&dstRect))
603 0 : return false;
604 :
605 0 : IntRect clipRect;
606 0 : if (!aClipRect.ToIntRect(&clipRect))
607 0 : return false;
608 :
609 0 : if (!(aTexturedEffect->mTextureCoords == Rect(0.0f, 0.0f, 1.0f, 1.0f)))
610 0 : return false;
611 0 : if (aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16)
612 0 : return false;
613 :
614 0 : if (aDest == aBuffer && !clipRect.Contains(dstRect))
615 0 : return false;
616 0 : if (!IntRect(IntPoint(0, 0), aDest->GetSize()).Contains(dstRect))
617 0 : return false;
618 :
619 : uint8_t* dstData;
620 0 : IntSize dstSize;
621 : int32_t dstStride;
622 : SurfaceFormat dstFormat;
623 0 : if (aDest->LockBits(&dstData, &dstSize, &dstStride, &dstFormat)) {
624 0 : wrappingSource->ConvertAndScale(dstFormat,
625 0 : dstRect.Size(),
626 0 : dstData + ptrdiff_t(dstRect.x) * BytesPerPixel(dstFormat) + ptrdiff_t(dstRect.y) * dstStride,
627 0 : dstStride);
628 0 : aDest->ReleaseBits(dstData);
629 0 : return true;
630 : } else
631 : #endif // MOZILLA_SSE_HAVE_CPUID_DETECTION
632 0 : return false;
633 : }
634 :
635 : void
636 61 : BasicCompositor::DrawQuad(const gfx::Rect& aRect,
637 : const gfx::IntRect& aClipRect,
638 : const EffectChain &aEffectChain,
639 : gfx::Float aOpacity,
640 : const gfx::Matrix4x4& aTransform,
641 : const gfx::Rect& aVisibleRect)
642 : {
643 : DrawGeometry(aRect, aRect, aClipRect, aEffectChain,
644 61 : aOpacity, aTransform, aVisibleRect, true);
645 61 : }
646 :
647 : void
648 0 : BasicCompositor::DrawPolygon(const gfx::Polygon& aPolygon,
649 : const gfx::Rect& aRect,
650 : const gfx::IntRect& aClipRect,
651 : const EffectChain& aEffectChain,
652 : gfx::Float aOpacity,
653 : const gfx::Matrix4x4& aTransform,
654 : const gfx::Rect& aVisibleRect)
655 : {
656 : DrawGeometry(aPolygon, aRect, aClipRect, aEffectChain,
657 0 : aOpacity, aTransform, aVisibleRect, false);
658 0 : }
659 :
660 :
661 : template<typename Geometry>
662 : void
663 61 : BasicCompositor::DrawGeometry(const Geometry& aGeometry,
664 : const gfx::Rect& aRect,
665 : const gfx::IntRect& aClipRect,
666 : const EffectChain& aEffectChain,
667 : gfx::Float aOpacity,
668 : const gfx::Matrix4x4& aTransform,
669 : const gfx::Rect& aVisibleRect,
670 : const bool aEnableAA)
671 : {
672 122 : RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget;
673 :
674 : // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing,
675 : // |dest| is a temporary surface.
676 122 : RefPtr<DrawTarget> dest = buffer;
677 :
678 122 : AutoRestoreTransform autoRestoreTransform(dest);
679 :
680 61 : Matrix newTransform;
681 61 : Rect transformBounds;
682 61 : Matrix4x4 new3DTransform;
683 61 : IntPoint offset = mRenderTarget->GetOrigin();
684 :
685 61 : if (aTransform.Is2D()) {
686 61 : newTransform = aTransform.As2D();
687 : } else {
688 : // Create a temporary surface for the transform.
689 0 : dest = Factory::CreateDrawTarget(gfxVars::ContentBackend(), RoundedOut(aRect).Size(), SurfaceFormat::B8G8R8A8);
690 0 : if (!dest) {
691 0 : return;
692 : }
693 :
694 0 : dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y));
695 :
696 : // Get the bounds post-transform.
697 0 : transformBounds = aTransform.TransformAndClipBounds(aRect, Rect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height));
698 0 : transformBounds.RoundOut();
699 :
700 0 : if (transformBounds.IsEmpty()) {
701 0 : return;
702 : }
703 :
704 0 : newTransform = Matrix();
705 :
706 : // When we apply the 3D transformation, we do it against a temporary
707 : // surface, so undo the coordinate offset.
708 0 : new3DTransform = aTransform;
709 0 : new3DTransform.PreTranslate(aRect.x, aRect.y, 0);
710 : }
711 :
712 : // XXX the transform is probably just an integer offset so this whole
713 : // business here is a bit silly.
714 61 : Rect transformedClipRect = buffer->GetTransform().TransformBounds(Rect(aClipRect));
715 :
716 61 : buffer->PushClipRect(Rect(aClipRect));
717 :
718 61 : newTransform.PostTranslate(-offset.x, -offset.y);
719 61 : buffer->SetTransform(newTransform);
720 :
721 122 : RefPtr<SourceSurface> sourceMask;
722 61 : Matrix maskTransform;
723 61 : if (aTransform.Is2D()) {
724 61 : SetupMask(aEffectChain, dest, offset, sourceMask, maskTransform);
725 : }
726 :
727 61 : CompositionOp blendMode = CompositionOp::OP_OVER;
728 61 : if (Effect* effect = aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()) {
729 0 : blendMode = static_cast<EffectBlendMode*>(effect)->mBlendMode;
730 : }
731 :
732 : const AntialiasMode aaMode =
733 61 : aEnableAA ? AntialiasMode::DEFAULT : AntialiasMode::NONE;
734 :
735 61 : DrawOptions drawOptions(aOpacity, blendMode, aaMode);
736 :
737 61 : switch (aEffectChain.mPrimaryEffect->mType) {
738 : case EffectTypes::SOLID_COLOR: {
739 : EffectSolidColor* effectSolidColor =
740 5 : static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get());
741 :
742 5 : bool unboundedOp = !IsOperatorBoundByMask(blendMode);
743 5 : if (unboundedOp) {
744 0 : dest->PushClipRect(aRect);
745 : }
746 :
747 5 : DrawSurface(dest, aGeometry, aRect, effectSolidColor->mColor,
748 : drawOptions, sourceMask, &maskTransform);
749 :
750 5 : if (unboundedOp) {
751 0 : dest->PopClip();
752 : }
753 5 : break;
754 : }
755 : case EffectTypes::RGB: {
756 : TexturedEffect* texturedEffect =
757 56 : static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
758 56 : TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic();
759 :
760 56 : if (source && texturedEffect->mPremultiplied) {
761 : // we have a fast path for video here
762 112 : if (source->mFromYCBCR &&
763 0 : AttemptVideoConvertAndScale(texturedEffect->mTexture, sourceMask, aOpacity, blendMode,
764 : texturedEffect,
765 : newTransform, aRect, transformedClipRect,
766 : dest, buffer)) {
767 : // we succeeded in convert and scaling
768 56 : } else if (source->mFromYCBCR &&
769 0 : !source->GetSurface(dest)) {
770 0 : gfxWarning() << "Failed to get YCbCr to rgb surface.";
771 56 : } else if (source->mFromYCBCR &&
772 0 : AttemptVideoScale(source, sourceMask, aOpacity, blendMode,
773 : texturedEffect,
774 : newTransform, aRect, transformedClipRect,
775 : dest, buffer)) {
776 : // we succeeded in scaling
777 : } else {
778 112 : DrawSurfaceWithTextureCoords(dest, aGeometry, aRect,
779 56 : source->GetSurface(dest),
780 : texturedEffect->mTextureCoords,
781 : texturedEffect->mSamplingFilter,
782 : drawOptions,
783 : sourceMask, &maskTransform);
784 : }
785 0 : } else if (source) {
786 0 : SourceSurface* srcSurf = source->GetSurface(dest);
787 0 : if (srcSurf) {
788 0 : RefPtr<DataSourceSurface> srcData = srcSurf->GetDataSurface();
789 :
790 : // Yes, we re-create the premultiplied data every time.
791 : // This might be better with a cache, eventually.
792 0 : RefPtr<DataSourceSurface> premultData = gfxUtils::CreatePremultipliedDataSurface(srcData);
793 :
794 0 : DrawSurfaceWithTextureCoords(dest, aGeometry, aRect,
795 : premultData,
796 : texturedEffect->mTextureCoords,
797 : texturedEffect->mSamplingFilter,
798 : drawOptions,
799 : sourceMask, &maskTransform);
800 : }
801 : } else {
802 0 : gfxDevCrash(LogReason::IncompatibleBasicTexturedEffect) << "Bad for basic with " << texturedEffect->mTexture->Name() << " and " << gfx::hexa(sourceMask);
803 : }
804 :
805 56 : break;
806 : }
807 : case EffectTypes::YCBCR: {
808 0 : MOZ_CRASH("Can't (easily) support component alpha with BasicCompositor!");
809 : break;
810 : }
811 : case EffectTypes::RENDER_TARGET: {
812 : EffectRenderTarget* effectRenderTarget =
813 0 : static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get());
814 : RefPtr<BasicCompositingRenderTarget> surface
815 0 : = static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get());
816 0 : RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot();
817 :
818 0 : DrawSurfaceWithTextureCoords(dest, aGeometry, aRect,
819 : sourceSurf,
820 : effectRenderTarget->mTextureCoords,
821 : effectRenderTarget->mSamplingFilter,
822 : drawOptions,
823 : sourceMask, &maskTransform);
824 0 : break;
825 : }
826 : case EffectTypes::COMPONENT_ALPHA: {
827 0 : MOZ_CRASH("Can't (easily) support component alpha with BasicCompositor!");
828 : break;
829 : }
830 : default: {
831 0 : MOZ_CRASH("Invalid effect type!");
832 : break;
833 : }
834 : }
835 :
836 61 : if (!aTransform.Is2D()) {
837 0 : dest->Flush();
838 :
839 0 : RefPtr<SourceSurface> destSnapshot = dest->Snapshot();
840 :
841 0 : SetupMask(aEffectChain, buffer, offset, sourceMask, maskTransform);
842 :
843 0 : if (sourceMask) {
844 : RefPtr<DrawTarget> transformDT =
845 0 : dest->CreateSimilarDrawTarget(IntSize::Truncate(transformBounds.width, transformBounds.height),
846 0 : SurfaceFormat::B8G8R8A8);
847 0 : new3DTransform.PostTranslate(-transformBounds.x, -transformBounds.y, 0);
848 0 : if (transformDT &&
849 0 : transformDT->Draw3DTransformedSurface(destSnapshot, new3DTransform)) {
850 0 : RefPtr<SourceSurface> transformSnapshot = transformDT->Snapshot();
851 :
852 : // Transform the source by it's normal transform, and then the inverse
853 : // of the mask transform so that it's in the mask's untransformed
854 : // coordinate space.
855 0 : Matrix sourceTransform = newTransform;
856 0 : sourceTransform.PostTranslate(transformBounds.TopLeft());
857 :
858 0 : Matrix inverseMask = maskTransform;
859 0 : inverseMask.Invert();
860 :
861 0 : sourceTransform *= inverseMask;
862 :
863 0 : SurfacePattern source(transformSnapshot, ExtendMode::CLAMP, sourceTransform);
864 :
865 0 : buffer->PushClipRect(transformBounds);
866 :
867 : // Mask in the untransformed coordinate space, and then transform
868 : // by the mask transform to put the result back into destination
869 : // coords.
870 0 : buffer->SetTransform(maskTransform);
871 0 : buffer->MaskSurface(source, sourceMask, Point(0, 0));
872 :
873 0 : buffer->PopClip();
874 : }
875 : } else {
876 0 : buffer->Draw3DTransformedSurface(destSnapshot, new3DTransform);
877 : }
878 : }
879 :
880 61 : buffer->PopClip();
881 : }
882 :
883 : void
884 0 : BasicCompositor::ClearRect(const gfx::Rect& aRect)
885 : {
886 0 : mRenderTarget->mDrawTarget->ClearRect(aRect);
887 0 : }
888 :
889 : void
890 27 : BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion,
891 : const gfx::IntRect *aClipRectIn,
892 : const gfx::IntRect& aRenderBounds,
893 : const nsIntRegion& aOpaqueRegion,
894 : gfx::IntRect *aClipRectOut /* = nullptr */,
895 : gfx::IntRect *aRenderBoundsOut /* = nullptr */)
896 : {
897 27 : if (mIsPendingEndRemoteDrawing) {
898 : // Force to end previous remote drawing.
899 0 : TryToEndRemoteDrawing(/* aForceToEnd */ true);
900 0 : MOZ_ASSERT(!mIsPendingEndRemoteDrawing);
901 : }
902 :
903 27 : LayoutDeviceIntRect intRect(LayoutDeviceIntPoint(), mWidget->GetClientSize());
904 27 : IntRect rect = IntRect(0, 0, intRect.width, intRect.height);
905 :
906 54 : LayoutDeviceIntRegion invalidRegionSafe;
907 : // Sometimes the invalid region is larger than we want to draw.
908 : invalidRegionSafe.And(
909 27 : LayoutDeviceIntRegion::FromUnknownRegion(aInvalidRegion), intRect);
910 :
911 27 : mInvalidRegion = invalidRegionSafe;
912 27 : mInvalidRect = mInvalidRegion.GetBounds();
913 :
914 27 : if (aRenderBoundsOut) {
915 27 : *aRenderBoundsOut = IntRect();
916 : }
917 :
918 27 : BufferMode bufferMode = BufferMode::BUFFERED;
919 27 : if (mTarget) {
920 : // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy
921 : // placeholder so that CreateRenderTarget() works. This is only used to create a new buffered
922 : // draw target that we composite into, then copy the results the destination.
923 0 : mDrawTarget = mTarget;
924 0 : bufferMode = BufferMode::BUFFER_NONE;
925 : } else {
926 : // StartRemoteDrawingInRegion can mutate mInvalidRegion.
927 27 : mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion, &bufferMode);
928 27 : if (!mDrawTarget) {
929 0 : return;
930 : }
931 27 : mInvalidRect = mInvalidRegion.GetBounds();
932 27 : if (mInvalidRect.IsEmpty()) {
933 0 : mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
934 0 : return;
935 : }
936 : }
937 :
938 27 : if (!mDrawTarget || mInvalidRect.IsEmpty()) {
939 0 : return;
940 : }
941 :
942 27 : LayoutDeviceIntRect clearRect;
943 27 : if (!aOpaqueRegion.IsEmpty()) {
944 54 : LayoutDeviceIntRegion clearRegion = mInvalidRegion;
945 27 : clearRegion.SubOut(LayoutDeviceIntRegion::FromUnknownRegion(aOpaqueRegion));
946 27 : clearRect = clearRegion.GetBounds();
947 : } else {
948 0 : clearRect = mInvalidRect;
949 : }
950 :
951 : // Prevent CreateRenderTargetForWindow from clearing unwanted area.
952 27 : gfxUtils::ClipToRegion(mDrawTarget,
953 54 : mInvalidRegion.ToUnknownRegion());
954 :
955 : // Setup an intermediate render target to buffer all compositing. We will
956 : // copy this into mDrawTarget (the widget), and/or mTarget in EndFrame()
957 : RefPtr<CompositingRenderTarget> target =
958 54 : CreateRenderTargetForWindow(mInvalidRect,
959 : clearRect,
960 81 : bufferMode);
961 :
962 27 : mDrawTarget->PopClip();
963 :
964 27 : if (!target) {
965 0 : if (!mTarget) {
966 0 : mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
967 : }
968 0 : return;
969 : }
970 27 : SetRenderTarget(target);
971 :
972 : // We only allocate a surface sized to the invalidated region, so we need to
973 : // translate future coordinates.
974 27 : mRenderTarget->mDrawTarget->SetTransform(Matrix::Translation(-mRenderTarget->GetOrigin()));
975 :
976 27 : gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget,
977 54 : mInvalidRegion.ToUnknownRegion());
978 :
979 27 : if (aRenderBoundsOut) {
980 27 : *aRenderBoundsOut = rect;
981 : }
982 :
983 27 : if (aClipRectIn) {
984 0 : mRenderTarget->mDrawTarget->PushClipRect(Rect(*aClipRectIn));
985 : } else {
986 27 : mRenderTarget->mDrawTarget->PushClipRect(Rect(rect));
987 27 : if (aClipRectOut) {
988 27 : *aClipRectOut = rect;
989 : }
990 : }
991 : }
992 :
993 : void
994 27 : BasicCompositor::EndFrame()
995 : {
996 27 : Compositor::EndFrame();
997 :
998 : // Pop aClipRectIn/bounds rect
999 27 : mRenderTarget->mDrawTarget->PopClip();
1000 :
1001 27 : if (gfxPrefs::WidgetUpdateFlashing()) {
1002 0 : float r = float(rand()) / RAND_MAX;
1003 0 : float g = float(rand()) / RAND_MAX;
1004 0 : float b = float(rand()) / RAND_MAX;
1005 : // We're still clipped to mInvalidRegion, so just fill the bounds.
1006 0 : mRenderTarget->mDrawTarget->FillRect(
1007 0 : IntRectToRect(mInvalidRegion.GetBounds()).ToUnknownRect(),
1008 0 : ColorPattern(Color(r, g, b, 0.2f)));
1009 : }
1010 :
1011 : // Pop aInvalidregion
1012 27 : mRenderTarget->mDrawTarget->PopClip();
1013 :
1014 27 : TryToEndRemoteDrawing();
1015 27 : }
1016 :
1017 : void
1018 27 : BasicCompositor::TryToEndRemoteDrawing(bool aForceToEnd)
1019 : {
1020 27 : if (mIsDestroyed || !mRenderTarget) {
1021 0 : return;
1022 : }
1023 :
1024 : // It it is not a good timing for EndRemoteDrawing, defter to call it.
1025 27 : if (!aForceToEnd && !mTarget && NeedsToDeferEndRemoteDrawing()) {
1026 0 : mIsPendingEndRemoteDrawing = true;
1027 :
1028 0 : const uint32_t retryMs = 2;
1029 0 : RefPtr<BasicCompositor> self = this;
1030 : RefPtr<Runnable> runnable =
1031 0 : NS_NewRunnableFunction("layers::BasicCompositor::TryToEndRemoteDrawing",
1032 0 : [self]() { self->TryToEndRemoteDrawing(); });
1033 0 : MessageLoop::current()->PostDelayedTask(runnable.forget(), retryMs);
1034 0 : return;
1035 : }
1036 :
1037 27 : if (mRenderTarget->mDrawTarget != mDrawTarget) {
1038 : // Note: Most platforms require us to buffer drawing to the widget surface.
1039 : // That's why we don't draw to mDrawTarget directly.
1040 0 : RefPtr<SourceSurface> source;
1041 0 : if (mRenderTarget->mDrawTarget != mDrawTarget) {
1042 0 : source = mWidget->EndBackBufferDrawing();
1043 : } else {
1044 0 : source = mRenderTarget->mDrawTarget->Snapshot();
1045 : }
1046 0 : RefPtr<DrawTarget> dest(mTarget ? mTarget : mDrawTarget);
1047 :
1048 0 : nsIntPoint offset = mTarget ? mTargetBounds.TopLeft() : nsIntPoint();
1049 :
1050 : // The source DrawTarget is clipped to the invalidation region, so we have
1051 : // to copy the individual rectangles in the region or else we'll draw blank
1052 : // pixels.
1053 0 : for (auto iter = mInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
1054 0 : const LayoutDeviceIntRect& r = iter.Get();
1055 0 : dest->CopySurface(source,
1056 0 : IntRect(r.x, r.y, r.width, r.height) - mRenderTarget->GetOrigin(),
1057 0 : IntPoint(r.x, r.y) - offset);
1058 : }
1059 : }
1060 :
1061 27 : if (aForceToEnd || !mTarget) {
1062 27 : mWidget->EndRemoteDrawingInRegion(mDrawTarget, mInvalidRegion);
1063 : }
1064 :
1065 27 : mDrawTarget = nullptr;
1066 27 : mRenderTarget = nullptr;
1067 27 : mIsPendingEndRemoteDrawing = false;
1068 : }
1069 :
1070 : bool
1071 27 : BasicCompositor::NeedsToDeferEndRemoteDrawing()
1072 : {
1073 27 : MOZ_ASSERT(mDrawTarget);
1074 27 : MOZ_ASSERT(mRenderTarget);
1075 :
1076 27 : if (mTarget || mRenderTarget->mDrawTarget == mDrawTarget) {
1077 27 : return false;
1078 : }
1079 :
1080 0 : return mWidget->NeedsToDeferEndRemoteDrawing();
1081 : }
1082 :
1083 : void
1084 0 : BasicCompositor::FinishPendingComposite()
1085 : {
1086 0 : TryToEndRemoteDrawing(/* aForceToEnd */ true);
1087 0 : }
1088 :
1089 : } // namespace layers
1090 : } // namespace mozilla
|