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 "mozilla/layers/CompositionRecorder.h"
9 #include "base/message_loop.h" // for MessageLoop
10 #include "mozilla/gfx/Types.h"
11 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
12 #include "mozilla/layers/Diagnostics.h"
13 #include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
14 #include "mozilla/layers/TextureClient.h"
15 #include "mozilla/layers/TextureHost.h"
16 #include "mozilla/layers/CompositorThread.h"
17 #include "mozilla/mozalloc.h" // for operator delete, etc
18 #include "GeckoProfiler.h"
19 #include "gfx2DGlue.h"
20 #include "nsAppRunner.h"
26 class CompositorRecordedFrame final
: public RecordedFrame
{
28 CompositorRecordedFrame(const TimeStamp
& aTimeStamp
,
29 RefPtr
<AsyncReadbackBuffer
>&& aBuffer
)
30 : RecordedFrame(aTimeStamp
), mBuffer(aBuffer
) {}
32 virtual already_AddRefed
<gfx::DataSourceSurface
> GetSourceSurface() override
{
34 return do_AddRef(mSurface
);
37 gfx::IntSize size
= mBuffer
->GetSize();
39 mSurface
= gfx::Factory::CreateDataSourceSurface(
40 size
, gfx::SurfaceFormat::B8G8R8A8
,
43 if (!mBuffer
->MapAndCopyInto(mSurface
, size
)) {
48 return do_AddRef(mSurface
);
52 RefPtr
<AsyncReadbackBuffer
> mBuffer
;
53 RefPtr
<gfx::DataSourceSurface
> mSurface
;
56 Compositor::Compositor(widget::CompositorWidget
* aWidget
)
59 #if defined(MOZ_WIDGET_ANDROID)
60 // If the default color isn't white for Fennec, there is a black
61 // flash before the first page of a tab is loaded.
63 mClearColor(ToDeviceColor(sRGBColor::OpaqueWhite()))
66 mClearColor(gfx::DeviceColor())
71 Compositor::~Compositor() {}
73 void Compositor::Destroy() {
76 TextureSourceProvider::Destroy();
80 void Compositor::EndFrame() { mLastCompositionEndTime
= TimeStamp::Now(); }
82 nsTArray
<TexturedVertex
> TexturedTrianglesToVertexArray(
83 const nsTArray
<gfx::TexturedTriangle
>& aTriangles
) {
84 const auto VertexFromPoints
= [](const gfx::Point
& p
, const gfx::Point
& t
) {
85 return TexturedVertex
{{p
.x
, p
.y
}, {t
.x
, t
.y
}};
88 nsTArray
<TexturedVertex
> vertices
;
90 for (const gfx::TexturedTriangle
& t
: aTriangles
) {
91 vertices
.AppendElement(VertexFromPoints(t
.p1
, t
.textureCoords
.p1
));
92 vertices
.AppendElement(VertexFromPoints(t
.p2
, t
.textureCoords
.p2
));
93 vertices
.AppendElement(VertexFromPoints(t
.p3
, t
.textureCoords
.p3
));
99 static float WrapTexCoord(float v
) {
100 // This should return values in range [0, 1.0)
101 return v
- floorf(v
);
104 static void SetRects(size_t n
, decomposedRectArrayT
* aLayerRects
,
105 decomposedRectArrayT
* aTextureRects
, float x0
, float y0
,
106 float x1
, float y1
, float tx0
, float ty0
, float tx1
,
107 float ty1
, bool flip_y
) {
111 (*aLayerRects
)[n
] = gfx::Rect(x0
, y0
, x1
- x0
, y1
- y0
);
112 (*aTextureRects
)[n
] = gfx::Rect(tx0
, ty0
, tx1
- tx0
, ty1
- ty0
);
116 static inline bool FuzzyEqual(float a
, float b
) {
117 return fabs(a
- b
) < 0.0001f
;
119 static inline bool FuzzyLTE(float a
, float b
) { return a
<= b
+ 0.0001f
; }
122 size_t DecomposeIntoNoRepeatRects(const gfx::Rect
& aRect
,
123 const gfx::Rect
& aTexCoordRect
,
124 decomposedRectArrayT
* aLayerRects
,
125 decomposedRectArrayT
* aTextureRects
) {
126 gfx::Rect texCoordRect
= aTexCoordRect
;
128 // If the texture should be flipped, it will have negative height. Detect that
129 // here and compensate for it. We will flip each rect as we emit it.
130 bool flipped
= false;
131 if (texCoordRect
.Height() < 0) {
133 texCoordRect
.MoveByY(texCoordRect
.Height());
134 texCoordRect
.SetHeight(-texCoordRect
.Height());
137 // Wrap the texture coordinates so they are within [0,1] and cap width/height
138 // at 1. We rely on this below.
139 texCoordRect
= gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect
.X()),
140 WrapTexCoord(texCoordRect
.Y())),
141 gfx::Size(std::min(texCoordRect
.Width(), 1.0f
),
142 std::min(texCoordRect
.Height(), 1.0f
)));
145 texCoordRect
.X() >= 0.0f
&& texCoordRect
.X() <= 1.0f
&&
146 texCoordRect
.Y() >= 0.0f
&& texCoordRect
.Y() <= 1.0f
&&
147 texCoordRect
.Width() >= 0.0f
&& texCoordRect
.Width() <= 1.0f
&&
148 texCoordRect
.Height() >= 0.0f
&& texCoordRect
.Height() <= 1.0f
&&
149 texCoordRect
.XMost() >= 0.0f
&& texCoordRect
.XMost() <= 2.0f
&&
150 texCoordRect
.YMost() >= 0.0f
&& texCoordRect
.YMost() <= 2.0f
,
151 "We just wrapped the texture coordinates, didn't we?");
153 // Get the top left and bottom right points of the rectangle. Note that
154 // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
155 gfx::Point tl
= texCoordRect
.TopLeft();
156 gfx::Point br
= texCoordRect
.BottomRight();
158 NS_ASSERTION(tl
.x
>= 0.0f
&& tl
.x
<= 1.0f
&& tl
.y
>= 0.0f
&& tl
.y
<= 1.0f
&&
159 br
.x
>= tl
.x
&& br
.x
<= 2.0f
&& br
.y
>= tl
.y
&&
160 br
.y
<= 2.0f
&& FuzzyLTE(br
.x
- tl
.x
, 1.0f
) &&
161 FuzzyLTE(br
.y
- tl
.y
, 1.0f
),
162 "Somehow generated invalid texture coordinates");
164 // Then check if we wrap in either the x or y axis.
165 bool xwrap
= br
.x
> 1.0f
;
166 bool ywrap
= br
.y
> 1.0f
;
168 // If xwrap is false, the texture will be sampled from tl.x .. br.x.
169 // If xwrap is true, then it will be split into tl.x .. 1.0, and
170 // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
171 // rectangle is also split appropriately, according to the calculated
173 if (!xwrap
&& !ywrap
) {
174 SetRects(0, aLayerRects
, aTextureRects
, aRect
.X(), aRect
.Y(), aRect
.XMost(),
175 aRect
.YMost(), tl
.x
, tl
.y
, br
.x
, br
.y
, flipped
);
179 // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
180 // wrap them here as well.
181 br
= gfx::Point(xwrap
? WrapTexCoord(br
.x
.value
) : br
.x
.value
,
182 ywrap
? WrapTexCoord(br
.y
.value
) : br
.y
.value
);
184 // If we wrap around along the x axis, we will draw first from
185 // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
186 // The same applies for the Y axis. The midpoints we calculate here are
187 // only valid if we actually wrap around.
189 aRect
.X() + (1.0f
- tl
.x
) / texCoordRect
.Width() * aRect
.Width();
191 aRect
.Y() + (1.0f
- tl
.y
) / texCoordRect
.Height() * aRect
.Height();
193 // Due to floating-point inaccuracy, we have to use XMost()-x and YMost()-y
194 // to calculate width and height, respectively, to ensure that size will
195 // remain consistent going from absolute to relative and back again.
197 !xwrap
|| (xmid
>= aRect
.X() && xmid
<= aRect
.XMost() &&
198 FuzzyEqual((xmid
- aRect
.X()) + (aRect
.XMost() - xmid
),
199 aRect
.XMost() - aRect
.X())),
200 "xmid should be within [x,XMost()] and the wrapped rect should have the "
203 !ywrap
|| (ymid
>= aRect
.Y() && ymid
<= aRect
.YMost() &&
204 FuzzyEqual((ymid
- aRect
.Y()) + (aRect
.YMost() - ymid
),
205 aRect
.YMost() - aRect
.Y())),
206 "ymid should be within [y,YMost()] and the wrapped rect should have the "
209 if (!xwrap
&& ywrap
) {
210 SetRects(0, aLayerRects
, aTextureRects
, aRect
.X(), aRect
.Y(), aRect
.XMost(),
211 ymid
, tl
.x
, tl
.y
, br
.x
, 1.0f
, flipped
);
212 SetRects(1, aLayerRects
, aTextureRects
, aRect
.X(), ymid
, aRect
.XMost(),
213 aRect
.YMost(), tl
.x
, 0.0f
, br
.x
, br
.y
, flipped
);
217 if (xwrap
&& !ywrap
) {
218 SetRects(0, aLayerRects
, aTextureRects
, aRect
.X(), aRect
.Y(), xmid
,
219 aRect
.YMost(), tl
.x
, tl
.y
, 1.0f
, br
.y
, flipped
);
220 SetRects(1, aLayerRects
, aTextureRects
, xmid
, aRect
.Y(), aRect
.XMost(),
221 aRect
.YMost(), 0.0f
, tl
.y
, br
.x
, br
.y
, flipped
);
225 SetRects(0, aLayerRects
, aTextureRects
, aRect
.X(), aRect
.Y(), xmid
, ymid
,
226 tl
.x
, tl
.y
, 1.0f
, 1.0f
, flipped
);
227 SetRects(1, aLayerRects
, aTextureRects
, xmid
, aRect
.Y(), aRect
.XMost(), ymid
,
228 0.0f
, tl
.y
, br
.x
, 1.0f
, flipped
);
229 SetRects(2, aLayerRects
, aTextureRects
, aRect
.X(), ymid
, xmid
, aRect
.YMost(),
230 tl
.x
, 0.0f
, 1.0f
, br
.y
, flipped
);
231 SetRects(3, aLayerRects
, aTextureRects
, xmid
, ymid
, aRect
.XMost(),
232 aRect
.YMost(), 0.0f
, 0.0f
, br
.x
, br
.y
, flipped
);
236 already_AddRefed
<RecordedFrame
> Compositor::RecordFrame(
237 const TimeStamp
& aTimeStamp
) {
238 RefPtr
<CompositingRenderTarget
> renderTarget
= GetWindowRenderTarget();
243 RefPtr
<AsyncReadbackBuffer
> buffer
=
244 CreateAsyncReadbackBuffer(renderTarget
->GetSize());
246 if (!ReadbackRenderTarget(renderTarget
, buffer
)) {
250 return MakeAndAddRef
<CompositorRecordedFrame
>(aTimeStamp
, std::move(buffer
));
253 bool Compositor::ShouldRecordFrames() const {
254 return profiler_feature_active(ProfilerFeature::Screenshots
) || mRecordFrames
;
257 } // namespace layers
258 } // namespace mozilla