Compositor.cpp (9274B)
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/. */ 6 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 "gfxUtils.h" 21 #include "nsAppRunner.h" 22 23 namespace mozilla::layers { 24 25 class CompositorRecordedFrame final : public RecordedFrame { 26 public: 27 CompositorRecordedFrame(const TimeStamp& aTimeStamp, 28 RefPtr<AsyncReadbackBuffer>&& aBuffer) 29 : RecordedFrame(aTimeStamp), mBuffer(aBuffer) {} 30 31 virtual already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override { 32 if (mSurface) { 33 return do_AddRef(mSurface); 34 } 35 36 gfx::IntSize size = mBuffer->GetSize(); 37 38 mSurface = gfx::Factory::CreateDataSourceSurface( 39 size, gfx::SurfaceFormat::B8G8R8A8, 40 /* aZero = */ false); 41 42 if (!mBuffer->MapAndCopyInto(mSurface, size)) { 43 mSurface = nullptr; 44 return nullptr; 45 } 46 47 return do_AddRef(mSurface); 48 } 49 50 private: 51 RefPtr<AsyncReadbackBuffer> mBuffer; 52 RefPtr<gfx::DataSourceSurface> mSurface; 53 }; 54 55 Compositor::Compositor(widget::CompositorWidget* aWidget) 56 : mWidget(aWidget), 57 mIsDestroyed(false), 58 #if defined(MOZ_WIDGET_ANDROID) 59 // If the default color isn't white for Fennec, there is a black 60 // flash before the first page of a tab is loaded. 61 mClearColor(gfx::ToDeviceColor(gfx::sRGBColor::OpaqueWhite())) 62 #else 63 mClearColor(gfx::DeviceColor()) 64 #endif 65 { 66 } 67 68 Compositor::~Compositor() {} 69 70 void Compositor::Destroy() { 71 mWidget = nullptr; 72 73 TextureSourceProvider::Destroy(); 74 mIsDestroyed = true; 75 } 76 77 void Compositor::EndFrame() { mLastCompositionEndTime = TimeStamp::Now(); } 78 79 nsTArray<TexturedVertex> TexturedTrianglesToVertexArray( 80 const nsTArray<gfx::TexturedTriangle>& aTriangles) { 81 const auto VertexFromPoints = [](const gfx::Point& p, const gfx::Point& t) { 82 return TexturedVertex{{p.x, p.y}, {t.x, t.y}}; 83 }; 84 85 nsTArray<TexturedVertex> vertices; 86 87 for (const gfx::TexturedTriangle& t : aTriangles) { 88 vertices.AppendElement(VertexFromPoints(t.p1, t.textureCoords.p1)); 89 vertices.AppendElement(VertexFromPoints(t.p2, t.textureCoords.p2)); 90 vertices.AppendElement(VertexFromPoints(t.p3, t.textureCoords.p3)); 91 } 92 93 return vertices; 94 } 95 96 static float WrapTexCoord(float v) { 97 // This should return values in range [0, 1.0) 98 return v - floorf(v); 99 } 100 101 static void SetRects(size_t n, decomposedRectArrayT* aLayerRects, 102 decomposedRectArrayT* aTextureRects, float x0, float y0, 103 float x1, float y1, float tx0, float ty0, float tx1, 104 float ty1, bool flip_y) { 105 if (flip_y) { 106 std::swap(ty0, ty1); 107 } 108 (*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0); 109 (*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0); 110 } 111 112 #ifdef DEBUG 113 static inline bool FuzzyEqual(float a, float b) { 114 return fabs(a - b) < 0.0001f; 115 } 116 static inline bool FuzzyLTE(float a, float b) { return a <= b + 0.0001f; } 117 #endif 118 119 size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect, 120 const gfx::Rect& aTexCoordRect, 121 decomposedRectArrayT* aLayerRects, 122 decomposedRectArrayT* aTextureRects) { 123 gfx::Rect texCoordRect = aTexCoordRect; 124 125 // If the texture should be flipped, it will have negative height. Detect that 126 // here and compensate for it. We will flip each rect as we emit it. 127 bool flipped = false; 128 if (texCoordRect.Height() < 0) { 129 flipped = true; 130 texCoordRect.MoveByY(texCoordRect.Height()); 131 texCoordRect.SetHeight(-texCoordRect.Height()); 132 } 133 134 // Wrap the texture coordinates so they are within [0,1] and cap width/height 135 // at 1. We rely on this below. 136 texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.X()), 137 WrapTexCoord(texCoordRect.Y())), 138 gfx::Size(std::min(texCoordRect.Width(), 1.0f), 139 std::min(texCoordRect.Height(), 1.0f))); 140 141 NS_ASSERTION( 142 texCoordRect.X() >= 0.0f && texCoordRect.X() <= 1.0f && 143 texCoordRect.Y() >= 0.0f && texCoordRect.Y() <= 1.0f && 144 texCoordRect.Width() >= 0.0f && texCoordRect.Width() <= 1.0f && 145 texCoordRect.Height() >= 0.0f && texCoordRect.Height() <= 1.0f && 146 texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f && 147 texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f, 148 "We just wrapped the texture coordinates, didn't we?"); 149 150 // Get the top left and bottom right points of the rectangle. Note that 151 // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2]. 152 gfx::Point tl = texCoordRect.TopLeft(); 153 gfx::Point br = texCoordRect.BottomRight(); 154 155 NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f && tl.y >= 0.0f && tl.y <= 1.0f && 156 br.x >= tl.x && br.x <= 2.0f && br.y >= tl.y && 157 br.y <= 2.0f && FuzzyLTE(br.x - tl.x, 1.0f) && 158 FuzzyLTE(br.y - tl.y, 1.0f), 159 "Somehow generated invalid texture coordinates"); 160 161 // Then check if we wrap in either the x or y axis. 162 bool xwrap = br.x > 1.0f; 163 bool ywrap = br.y > 1.0f; 164 165 // If xwrap is false, the texture will be sampled from tl.x .. br.x. 166 // If xwrap is true, then it will be split into tl.x .. 1.0, and 167 // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination 168 // rectangle is also split appropriately, according to the calculated 169 // xmid/ymid values. 170 if (!xwrap && !ywrap) { 171 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), aRect.XMost(), 172 aRect.YMost(), tl.x, tl.y, br.x, br.y, flipped); 173 return 1; 174 } 175 176 // If we are dealing with wrapping br.x and br.y are greater than 1.0 so 177 // wrap them here as well. 178 br = gfx::Point(xwrap ? WrapTexCoord(br.x.value) : br.x.value, 179 ywrap ? WrapTexCoord(br.y.value) : br.y.value); 180 181 // If we wrap around along the x axis, we will draw first from 182 // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above). 183 // The same applies for the Y axis. The midpoints we calculate here are 184 // only valid if we actually wrap around. 185 GLfloat xmid = 186 aRect.X() + (1.0f - tl.x) / texCoordRect.Width() * aRect.Width(); 187 GLfloat ymid = 188 aRect.Y() + (1.0f - tl.y) / texCoordRect.Height() * aRect.Height(); 189 190 // Due to floating-point inaccuracy, we have to use XMost()-x and YMost()-y 191 // to calculate width and height, respectively, to ensure that size will 192 // remain consistent going from absolute to relative and back again. 193 NS_ASSERTION( 194 !xwrap || (xmid >= aRect.X() && xmid <= aRect.XMost() && 195 FuzzyEqual((xmid - aRect.X()) + (aRect.XMost() - xmid), 196 aRect.XMost() - aRect.X())), 197 "xmid should be within [x,XMost()] and the wrapped rect should have the " 198 "same width"); 199 NS_ASSERTION( 200 !ywrap || (ymid >= aRect.Y() && ymid <= aRect.YMost() && 201 FuzzyEqual((ymid - aRect.Y()) + (aRect.YMost() - ymid), 202 aRect.YMost() - aRect.Y())), 203 "ymid should be within [y,YMost()] and the wrapped rect should have the " 204 "same height"); 205 206 if (!xwrap && ywrap) { 207 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), aRect.XMost(), 208 ymid, tl.x, tl.y, br.x, 1.0f, flipped); 209 SetRects(1, aLayerRects, aTextureRects, aRect.X(), ymid, aRect.XMost(), 210 aRect.YMost(), tl.x, 0.0f, br.x, br.y, flipped); 211 return 2; 212 } 213 214 if (xwrap && !ywrap) { 215 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), xmid, 216 aRect.YMost(), tl.x, tl.y, 1.0f, br.y, flipped); 217 SetRects(1, aLayerRects, aTextureRects, xmid, aRect.Y(), aRect.XMost(), 218 aRect.YMost(), 0.0f, tl.y, br.x, br.y, flipped); 219 return 2; 220 } 221 222 SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), xmid, ymid, 223 tl.x, tl.y, 1.0f, 1.0f, flipped); 224 SetRects(1, aLayerRects, aTextureRects, xmid, aRect.Y(), aRect.XMost(), ymid, 225 0.0f, tl.y, br.x, 1.0f, flipped); 226 SetRects(2, aLayerRects, aTextureRects, aRect.X(), ymid, xmid, aRect.YMost(), 227 tl.x, 0.0f, 1.0f, br.y, flipped); 228 SetRects(3, aLayerRects, aTextureRects, xmid, ymid, aRect.XMost(), 229 aRect.YMost(), 0.0f, 0.0f, br.x, br.y, flipped); 230 return 4; 231 } 232 233 bool Compositor::ShouldRecordFrames() const { 234 return profiler_feature_active(ProfilerFeature::Screenshots) || mRecordFrames; 235 } 236 237 } // namespace mozilla::layers