Bug 1632310 [wpt PR 23186] - Add test for computed versus resolved style., a=testonly
[gecko.git] / gfx / layers / Compositor.cpp
blob5825bda1b1699ec5a02f8fcd95d03e34c9103895
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/layers/Compositor.h"
8 #include "base/message_loop.h" // for MessageLoop
9 #include "mozilla/gfx/Types.h"
10 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
11 #include "mozilla/layers/Diagnostics.h"
12 #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
13 #include "mozilla/layers/TextureClient.h"
14 #include "mozilla/layers/TextureHost.h"
15 #include "mozilla/layers/CompositorThread.h"
16 #include "mozilla/mozalloc.h" // for operator delete, etc
17 #include "GeckoProfiler.h"
18 #include "gfx2DGlue.h"
19 #include "nsAppRunner.h"
20 #include "LayersHelpers.h"
22 namespace mozilla {
24 namespace layers {
26 class CompositorRecordedFrame final : public RecordedFrame {
27 public:
28 CompositorRecordedFrame(const TimeStamp& aTimeStamp,
29 RefPtr<AsyncReadbackBuffer>&& aBuffer)
30 : RecordedFrame(aTimeStamp), mBuffer(aBuffer) {}
32 virtual already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override {
33 if (mSurface) {
34 return do_AddRef(mSurface);
37 gfx::IntSize size = mBuffer->GetSize();
39 mSurface = gfx::Factory::CreateDataSourceSurface(
40 size, gfx::SurfaceFormat::B8G8R8A8,
41 /* aZero = */ false);
43 if (!mBuffer->MapAndCopyInto(mSurface, size)) {
44 mSurface = nullptr;
45 return nullptr;
48 return do_AddRef(mSurface);
51 private:
52 RefPtr<AsyncReadbackBuffer> mBuffer;
53 RefPtr<gfx::DataSourceSurface> mSurface;
56 Compositor::Compositor(widget::CompositorWidget* aWidget,
57 CompositorBridgeParent* aParent)
58 : mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC),
59 mParent(aParent),
60 mPixelsPerFrame(0),
61 mPixelsFilled(0),
62 mScreenRotation(ROTATION_0),
63 mWidget(aWidget),
64 mIsDestroyed(false)
65 #if defined(MOZ_WIDGET_ANDROID)
66 // If the default color isn't white for Fennec, there is a black
67 // flash before the first page of a tab is loaded.
69 mClearColor(ToDeviceColor(sRGBColor::OpaqueWhite())),
70 mDefaultClearColor(ToDeviceColor(sRGBColor::OpaqueWhite()))
71 #else
73 mClearColor(gfx::DeviceColor()),
74 mDefaultClearColor(gfx::DeviceColor())
75 #endif
79 Compositor::~Compositor() { ReadUnlockTextures(); }
81 void Compositor::Destroy() {
82 mWidget = nullptr;
84 TextureSourceProvider::Destroy();
85 mIsDestroyed = true;
88 void Compositor::EndFrame() {
89 ReadUnlockTextures();
90 mLastCompositionEndTime = TimeStamp::Now();
93 bool Compositor::ShouldDrawDiagnostics(DiagnosticFlags aFlags) {
94 if ((aFlags & DiagnosticFlags::TILE) &&
95 !(mDiagnosticTypes & DiagnosticTypes::TILE_BORDERS)) {
96 return false;
98 if ((aFlags & DiagnosticFlags::BIGIMAGE) &&
99 !(mDiagnosticTypes & DiagnosticTypes::BIGIMAGE_BORDERS)) {
100 return false;
102 if (mDiagnosticTypes == DiagnosticTypes::NO_DIAGNOSTIC) {
103 return false;
105 return true;
108 void Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
109 const nsIntRegion& aVisibleRegion,
110 const gfx::IntRect& aClipRect,
111 const gfx::Matrix4x4& aTransform,
112 uint32_t aFlashCounter) {
113 if (!ShouldDrawDiagnostics(aFlags)) {
114 return;
117 if (aVisibleRegion.GetNumRects() > 1) {
118 for (auto iter = aVisibleRegion.RectIter(); !iter.Done(); iter.Next()) {
119 DrawDiagnostics(aFlags | DiagnosticFlags::REGION_RECT,
120 IntRectToRect(iter.Get()), aClipRect, aTransform,
121 aFlashCounter);
125 DrawDiagnostics(aFlags, IntRectToRect(aVisibleRegion.GetBounds()), aClipRect,
126 aTransform, aFlashCounter);
129 void Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
130 const gfx::Rect& aVisibleRect,
131 const gfx::IntRect& aClipRect,
132 const gfx::Matrix4x4& aTransform,
133 uint32_t aFlashCounter) {
134 if (!ShouldDrawDiagnostics(aFlags)) {
135 return;
138 DrawDiagnosticsInternal(aFlags, aVisibleRect, aClipRect, aTransform,
139 aFlashCounter);
142 void Compositor::DrawDiagnosticsInternal(DiagnosticFlags aFlags,
143 const gfx::Rect& aVisibleRect,
144 const gfx::IntRect& aClipRect,
145 const gfx::Matrix4x4& aTransform,
146 uint32_t aFlashCounter) {
147 #ifdef ANDROID
148 int lWidth = 10;
149 #else
150 int lWidth = 2;
151 #endif
153 // Technically it is sRGB but it is just for debugging.
154 gfx::DeviceColor color;
155 if (aFlags & DiagnosticFlags::CONTENT) {
156 color = gfx::DeviceColor(0.0f, 1.0f, 0.0f, 1.0f); // green
157 if (aFlags & DiagnosticFlags::COMPONENT_ALPHA) {
158 color = gfx::DeviceColor(0.0f, 1.0f, 1.0f, 1.0f); // greenish blue
160 } else if (aFlags & DiagnosticFlags::IMAGE) {
161 if (aFlags & DiagnosticFlags::NV12) {
162 color = gfx::DeviceColor(1.0f, 1.0f, 0.0f, 1.0f); // yellow
163 } else if (aFlags & DiagnosticFlags::YCBCR) {
164 color = gfx::DeviceColor(1.0f, 0.55f, 0.0f, 1.0f); // orange
165 } else {
166 color = gfx::DeviceColor(1.0f, 0.0f, 0.0f, 1.0f); // red
168 } else if (aFlags & DiagnosticFlags::COLOR) {
169 color = gfx::DeviceColor(0.0f, 0.0f, 1.0f, 1.0f); // blue
170 } else if (aFlags & DiagnosticFlags::CONTAINER) {
171 color = gfx::DeviceColor(0.8f, 0.0f, 0.8f, 1.0f); // purple
174 // make tile borders a bit more transparent to keep layer borders readable.
175 if (aFlags & DiagnosticFlags::TILE || aFlags & DiagnosticFlags::BIGIMAGE ||
176 aFlags & DiagnosticFlags::REGION_RECT) {
177 lWidth = 1;
178 color.r *= 0.7f;
179 color.g *= 0.7f;
180 color.b *= 0.7f;
181 color.a = color.a * 0.5f;
182 } else {
183 color.a = color.a * 0.7f;
186 if (mDiagnosticTypes & DiagnosticTypes::FLASH_BORDERS) {
187 float flash = (float)aFlashCounter / (float)DIAGNOSTIC_FLASH_COUNTER_MAX;
188 color.r *= flash;
189 color.g *= flash;
190 color.b *= flash;
193 SlowDrawRect(aVisibleRect, color, aClipRect, aTransform, lWidth);
196 static void UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
197 const gfx::Rect& aRect,
198 const gfx::Rect& aIntersection,
199 const gfx::Rect& aTextureCoords) {
200 // Calculate the relative offset of the intersection within the layer.
201 float dx = (aIntersection.X() - aRect.X()) / aRect.Width();
202 float dy = (aIntersection.Y() - aRect.Y()) / aRect.Height();
204 // Update the texture offset.
205 float x = aTextureCoords.X() + dx * aTextureCoords.Width();
206 float y = aTextureCoords.Y() + dy * aTextureCoords.Height();
208 // Scale the texture width and height.
209 float w = aTextureCoords.Width() * aIntersection.Width() / aRect.Width();
210 float h = aTextureCoords.Height() * aIntersection.Height() / aRect.Height();
212 static const auto Clamp = [](float& f) {
213 if (f >= 1.0f) f = 1.0f;
214 if (f <= 0.0f) f = 0.0f;
217 auto UpdatePoint = [&](const gfx::Point& p, gfx::Point& t) {
218 t.x = x + (p.x - aIntersection.X()) / aIntersection.Width() * w;
219 t.y = y + (p.y - aIntersection.Y()) / aIntersection.Height() * h;
221 Clamp(t.x);
222 Clamp(t.y);
225 UpdatePoint(aTriangle.p1, aTriangle.textureCoords.p1);
226 UpdatePoint(aTriangle.p2, aTriangle.textureCoords.p2);
227 UpdatePoint(aTriangle.p3, aTriangle.textureCoords.p3);
230 void Compositor::DrawGeometry(const gfx::Rect& aRect,
231 const gfx::IntRect& aClipRect,
232 const EffectChain& aEffectChain,
233 gfx::Float aOpacity,
234 const gfx::Matrix4x4& aTransform,
235 const gfx::Rect& aVisibleRect,
236 const Maybe<gfx::Polygon>& aGeometry) {
237 if (aRect.IsEmpty()) {
238 return;
241 if (!aGeometry || !SupportsLayerGeometry()) {
242 DrawQuad(aRect, aClipRect, aEffectChain, aOpacity, aTransform,
243 aVisibleRect);
244 return;
247 // Cull completely invisible polygons.
248 if (aRect.Intersect(aGeometry->BoundingBox()).IsEmpty()) {
249 return;
252 const gfx::Polygon clipped = aGeometry->ClipPolygon(aRect);
254 // Cull polygons with no area.
255 if (clipped.IsEmpty()) {
256 return;
259 DrawPolygon(clipped, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
260 aVisibleRect);
263 void Compositor::DrawTriangles(
264 const nsTArray<gfx::TexturedTriangle>& aTriangles, const gfx::Rect& aRect,
265 const gfx::IntRect& aClipRect, const EffectChain& aEffectChain,
266 gfx::Float aOpacity, const gfx::Matrix4x4& aTransform,
267 const gfx::Rect& aVisibleRect) {
268 for (const gfx::TexturedTriangle& triangle : aTriangles) {
269 DrawTriangle(triangle, aClipRect, aEffectChain, aOpacity, aTransform,
270 aVisibleRect);
274 nsTArray<gfx::TexturedTriangle> GenerateTexturedTriangles(
275 const gfx::Polygon& aPolygon, const gfx::Rect& aRect,
276 const gfx::Rect& aTexRect) {
277 nsTArray<gfx::TexturedTriangle> texturedTriangles;
279 gfx::Rect layerRects[4];
280 gfx::Rect textureRects[4];
281 size_t rects =
282 DecomposeIntoNoRepeatRects(aRect, aTexRect, &layerRects, &textureRects);
283 for (size_t i = 0; i < rects; ++i) {
284 const gfx::Rect& rect = layerRects[i];
285 const gfx::Rect& texRect = textureRects[i];
286 const gfx::Polygon clipped = aPolygon.ClipPolygon(rect);
288 if (clipped.IsEmpty()) {
289 continue;
292 for (const gfx::Triangle& triangle : clipped.ToTriangles()) {
293 const gfx::Rect intersection = rect.Intersect(triangle.BoundingBox());
295 // Cull completely invisible triangles.
296 if (intersection.IsEmpty()) {
297 continue;
300 MOZ_ASSERT(rect.Width() > 0.0f && rect.Height() > 0.0f);
301 MOZ_ASSERT(intersection.Width() > 0.0f && intersection.Height() > 0.0f);
303 // Since the texture was created for non-split geometry, we need to
304 // update the texture coordinates to account for the split.
305 gfx::TexturedTriangle t(triangle);
306 UpdateTextureCoordinates(t, rect, intersection, texRect);
307 texturedTriangles.AppendElement(std::move(t));
311 return texturedTriangles;
314 nsTArray<TexturedVertex> TexturedTrianglesToVertexArray(
315 const nsTArray<gfx::TexturedTriangle>& aTriangles) {
316 const auto VertexFromPoints = [](const gfx::Point& p, const gfx::Point& t) {
317 return TexturedVertex{{p.x, p.y}, {t.x, t.y}};
320 nsTArray<TexturedVertex> vertices;
322 for (const gfx::TexturedTriangle& t : aTriangles) {
323 vertices.AppendElement(VertexFromPoints(t.p1, t.textureCoords.p1));
324 vertices.AppendElement(VertexFromPoints(t.p2, t.textureCoords.p2));
325 vertices.AppendElement(VertexFromPoints(t.p3, t.textureCoords.p3));
328 return vertices;
331 void Compositor::DrawPolygon(const gfx::Polygon& aPolygon,
332 const gfx::Rect& aRect,
333 const gfx::IntRect& aClipRect,
334 const EffectChain& aEffectChain,
335 gfx::Float aOpacity,
336 const gfx::Matrix4x4& aTransform,
337 const gfx::Rect& aVisibleRect) {
338 nsTArray<gfx::TexturedTriangle> texturedTriangles;
340 TexturedEffect* texturedEffect =
341 aEffectChain.mPrimaryEffect->AsTexturedEffect();
343 if (texturedEffect) {
344 texturedTriangles = GenerateTexturedTriangles(
345 aPolygon, aRect, texturedEffect->mTextureCoords);
346 } else {
347 for (const gfx::Triangle& triangle : aPolygon.ToTriangles()) {
348 texturedTriangles.AppendElement(gfx::TexturedTriangle(triangle));
352 if (texturedTriangles.IsEmpty()) {
353 // Nothing to render.
354 return;
357 DrawTriangles(texturedTriangles, aRect, aClipRect, aEffectChain, aOpacity,
358 aTransform, aVisibleRect);
361 void Compositor::SlowDrawRect(const gfx::Rect& aRect,
362 const gfx::DeviceColor& aColor,
363 const gfx::IntRect& aClipRect,
364 const gfx::Matrix4x4& aTransform,
365 int aStrokeWidth) {
366 // TODO This should draw a rect using a single draw call but since
367 // this is only used for debugging overlays it's not worth optimizing ATM.
368 float opacity = 1.0f;
369 EffectChain effects;
371 effects.mPrimaryEffect = new EffectSolidColor(aColor);
372 // left
373 this->DrawQuad(gfx::Rect(aRect.X(), aRect.Y(), aStrokeWidth, aRect.Height()),
374 aClipRect, effects, opacity, aTransform);
375 // top
376 this->DrawQuad(gfx::Rect(aRect.X() + aStrokeWidth, aRect.Y(),
377 aRect.Width() - 2 * aStrokeWidth, aStrokeWidth),
378 aClipRect, effects, opacity, aTransform);
379 // right
380 this->DrawQuad(gfx::Rect(aRect.XMost() - aStrokeWidth, aRect.Y(),
381 aStrokeWidth, aRect.Height()),
382 aClipRect, effects, opacity, aTransform);
383 // bottom
384 this->DrawQuad(
385 gfx::Rect(aRect.X() + aStrokeWidth, aRect.YMost() - aStrokeWidth,
386 aRect.Width() - 2 * aStrokeWidth, aStrokeWidth),
387 aClipRect, effects, opacity, aTransform);
390 void Compositor::FillRect(const gfx::Rect& aRect,
391 const gfx::DeviceColor& aColor,
392 const gfx::IntRect& aClipRect,
393 const gfx::Matrix4x4& aTransform) {
394 float opacity = 1.0f;
395 EffectChain effects;
397 effects.mPrimaryEffect = new EffectSolidColor(aColor);
398 this->DrawQuad(aRect, aClipRect, effects, opacity, aTransform);
401 static float WrapTexCoord(float v) {
402 // This should return values in range [0, 1.0)
403 return v - floorf(v);
406 static void SetRects(size_t n, decomposedRectArrayT* aLayerRects,
407 decomposedRectArrayT* aTextureRects, float x0, float y0,
408 float x1, float y1, float tx0, float ty0, float tx1,
409 float ty1, bool flip_y) {
410 if (flip_y) {
411 std::swap(ty0, ty1);
413 (*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0);
414 (*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
417 #ifdef DEBUG
418 static inline bool FuzzyEqual(float a, float b) {
419 return fabs(a - b) < 0.0001f;
421 static inline bool FuzzyLTE(float a, float b) { return a <= b + 0.0001f; }
422 #endif
424 size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
425 const gfx::Rect& aTexCoordRect,
426 decomposedRectArrayT* aLayerRects,
427 decomposedRectArrayT* aTextureRects) {
428 gfx::Rect texCoordRect = aTexCoordRect;
430 // If the texture should be flipped, it will have negative height. Detect that
431 // here and compensate for it. We will flip each rect as we emit it.
432 bool flipped = false;
433 if (texCoordRect.Height() < 0) {
434 flipped = true;
435 texCoordRect.MoveByY(texCoordRect.Height());
436 texCoordRect.SetHeight(-texCoordRect.Height());
439 // Wrap the texture coordinates so they are within [0,1] and cap width/height
440 // at 1. We rely on this below.
441 texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.X()),
442 WrapTexCoord(texCoordRect.Y())),
443 gfx::Size(std::min(texCoordRect.Width(), 1.0f),
444 std::min(texCoordRect.Height(), 1.0f)));
446 NS_ASSERTION(
447 texCoordRect.X() >= 0.0f && texCoordRect.X() <= 1.0f &&
448 texCoordRect.Y() >= 0.0f && texCoordRect.Y() <= 1.0f &&
449 texCoordRect.Width() >= 0.0f && texCoordRect.Width() <= 1.0f &&
450 texCoordRect.Height() >= 0.0f && texCoordRect.Height() <= 1.0f &&
451 texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
452 texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
453 "We just wrapped the texture coordinates, didn't we?");
455 // Get the top left and bottom right points of the rectangle. Note that
456 // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
457 gfx::Point tl = texCoordRect.TopLeft();
458 gfx::Point br = texCoordRect.BottomRight();
460 NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f && tl.y >= 0.0f && tl.y <= 1.0f &&
461 br.x >= tl.x && br.x <= 2.0f && br.y >= tl.y &&
462 br.y <= 2.0f && FuzzyLTE(br.x - tl.x, 1.0f) &&
463 FuzzyLTE(br.y - tl.y, 1.0f),
464 "Somehow generated invalid texture coordinates");
466 // Then check if we wrap in either the x or y axis.
467 bool xwrap = br.x > 1.0f;
468 bool ywrap = br.y > 1.0f;
470 // If xwrap is false, the texture will be sampled from tl.x .. br.x.
471 // If xwrap is true, then it will be split into tl.x .. 1.0, and
472 // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
473 // rectangle is also split appropriately, according to the calculated
474 // xmid/ymid values.
475 if (!xwrap && !ywrap) {
476 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), aRect.XMost(),
477 aRect.YMost(), tl.x, tl.y, br.x, br.y, flipped);
478 return 1;
481 // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
482 // wrap them here as well.
483 br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x,
484 ywrap ? WrapTexCoord(br.y) : br.y);
486 // If we wrap around along the x axis, we will draw first from
487 // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
488 // The same applies for the Y axis. The midpoints we calculate here are
489 // only valid if we actually wrap around.
490 GLfloat xmid =
491 aRect.X() + (1.0f - tl.x) / texCoordRect.Width() * aRect.Width();
492 GLfloat ymid =
493 aRect.Y() + (1.0f - tl.y) / texCoordRect.Height() * aRect.Height();
495 // Due to floating-point inaccuracy, we have to use XMost()-x and YMost()-y
496 // to calculate width and height, respectively, to ensure that size will
497 // remain consistent going from absolute to relative and back again.
498 NS_ASSERTION(
499 !xwrap || (xmid >= aRect.X() && xmid <= aRect.XMost() &&
500 FuzzyEqual((xmid - aRect.X()) + (aRect.XMost() - xmid),
501 aRect.XMost() - aRect.X())),
502 "xmid should be within [x,XMost()] and the wrapped rect should have the "
503 "same width");
504 NS_ASSERTION(
505 !ywrap || (ymid >= aRect.Y() && ymid <= aRect.YMost() &&
506 FuzzyEqual((ymid - aRect.Y()) + (aRect.YMost() - ymid),
507 aRect.YMost() - aRect.Y())),
508 "ymid should be within [y,YMost()] and the wrapped rect should have the "
509 "same height");
511 if (!xwrap && ywrap) {
512 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), aRect.XMost(),
513 ymid, tl.x, tl.y, br.x, 1.0f, flipped);
514 SetRects(1, aLayerRects, aTextureRects, aRect.X(), ymid, aRect.XMost(),
515 aRect.YMost(), tl.x, 0.0f, br.x, br.y, flipped);
516 return 2;
519 if (xwrap && !ywrap) {
520 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), xmid,
521 aRect.YMost(), tl.x, tl.y, 1.0f, br.y, flipped);
522 SetRects(1, aLayerRects, aTextureRects, xmid, aRect.Y(), aRect.XMost(),
523 aRect.YMost(), 0.0f, tl.y, br.x, br.y, flipped);
524 return 2;
527 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), xmid, ymid,
528 tl.x, tl.y, 1.0f, 1.0f, flipped);
529 SetRects(1, aLayerRects, aTextureRects, xmid, aRect.Y(), aRect.XMost(), ymid,
530 0.0f, tl.y, br.x, 1.0f, flipped);
531 SetRects(2, aLayerRects, aTextureRects, aRect.X(), ymid, xmid, aRect.YMost(),
532 tl.x, 0.0f, 1.0f, br.y, flipped);
533 SetRects(3, aLayerRects, aTextureRects, xmid, ymid, aRect.XMost(),
534 aRect.YMost(), 0.0f, 0.0f, br.x, br.y, flipped);
535 return 4;
538 gfx::IntRect Compositor::ComputeBackdropCopyRect(
539 const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
540 const gfx::Matrix4x4& aTransform, gfx::Matrix4x4* aOutTransform,
541 gfx::Rect* aOutLayerQuad) {
542 // Compute the clip.
543 RefPtr<CompositingRenderTarget> currentRenderTarget =
544 GetCurrentRenderTarget();
545 gfx::IntPoint rtOffset = currentRenderTarget->GetOrigin();
546 gfx::IntSize rtSize = currentRenderTarget->GetSize();
548 return layers::ComputeBackdropCopyRect(aRect, aClipRect, aTransform,
549 gfx::IntRect(rtOffset, rtSize),
550 aOutTransform, aOutLayerQuad);
553 gfx::IntRect Compositor::ComputeBackdropCopyRect(
554 const gfx::Triangle& aTriangle, const gfx::IntRect& aClipRect,
555 const gfx::Matrix4x4& aTransform, gfx::Matrix4x4* aOutTransform,
556 gfx::Rect* aOutLayerQuad) {
557 gfx::Rect boundingBox = aTriangle.BoundingBox();
558 return ComputeBackdropCopyRect(boundingBox, aClipRect, aTransform,
559 aOutTransform, aOutLayerQuad);
562 void Compositor::SetInvalid() { mParent = nullptr; }
564 bool Compositor::IsValid() const { return !!mParent; }
566 void Compositor::UnlockAfterComposition(TextureHost* aTexture) {
567 TextureSourceProvider::UnlockAfterComposition(aTexture);
569 // If this is being called after we shutdown the compositor, we must finish
570 // read unlocking now to prevent a cycle.
571 if (IsDestroyed()) {
572 ReadUnlockTextures();
576 bool Compositor::NotifyNotUsedAfterComposition(TextureHost* aTextureHost) {
577 if (IsDestroyed() || AsBasicCompositor()) {
578 return false;
580 return TextureSourceProvider::NotifyNotUsedAfterComposition(aTextureHost);
583 void Compositor::GetFrameStats(GPUStats* aStats) {
584 aStats->mInvalidPixels = mPixelsPerFrame;
585 aStats->mPixelsFilled = mPixelsFilled;
588 already_AddRefed<RecordedFrame> Compositor::RecordFrame(
589 const TimeStamp& aTimeStamp) {
590 RefPtr<CompositingRenderTarget> renderTarget = GetWindowRenderTarget();
591 if (!renderTarget) {
592 return nullptr;
595 RefPtr<AsyncReadbackBuffer> buffer =
596 CreateAsyncReadbackBuffer(renderTarget->GetSize());
598 if (!ReadbackRenderTarget(renderTarget, buffer)) {
599 return nullptr;
602 return MakeAndAddRef<CompositorRecordedFrame>(aTimeStamp, std::move(buffer));
605 bool Compositor::ShouldRecordFrames() const {
606 #ifdef MOZ_GECKO_PROFILER
607 if (profiler_feature_active(ProfilerFeature::Screenshots)) {
608 return true;
610 #endif
611 return mRecordFrames;
614 } // namespace layers
615 } // namespace mozilla