зеркало из https://github.com/mozilla/gecko-dev.git
280 строки
10 KiB
C++
280 строки
10 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/layers/Compositor.h"
|
|
#include "mozilla/layers/CompositionRecorder.h"
|
|
#include "base/message_loop.h" // for MessageLoop
|
|
#include "mozilla/gfx/Types.h"
|
|
#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
|
|
#include "mozilla/layers/Diagnostics.h"
|
|
#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
|
|
#include "mozilla/layers/TextureClient.h"
|
|
#include "mozilla/layers/TextureHost.h"
|
|
#include "mozilla/layers/CompositorThread.h"
|
|
#include "mozilla/mozalloc.h" // for operator delete, etc
|
|
#include "GeckoProfiler.h"
|
|
#include "gfx2DGlue.h"
|
|
#include "nsAppRunner.h"
|
|
#include "LayersHelpers.h"
|
|
|
|
namespace mozilla {
|
|
|
|
namespace layers {
|
|
|
|
class CompositorRecordedFrame final : public RecordedFrame {
|
|
public:
|
|
CompositorRecordedFrame(const TimeStamp& aTimeStamp,
|
|
RefPtr<AsyncReadbackBuffer>&& aBuffer)
|
|
: RecordedFrame(aTimeStamp), mBuffer(aBuffer) {}
|
|
|
|
virtual already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override {
|
|
if (mSurface) {
|
|
return do_AddRef(mSurface);
|
|
}
|
|
|
|
gfx::IntSize size = mBuffer->GetSize();
|
|
|
|
mSurface = gfx::Factory::CreateDataSourceSurface(
|
|
size, gfx::SurfaceFormat::B8G8R8A8,
|
|
/* aZero = */ false);
|
|
|
|
if (!mBuffer->MapAndCopyInto(mSurface, size)) {
|
|
mSurface = nullptr;
|
|
return nullptr;
|
|
}
|
|
|
|
return do_AddRef(mSurface);
|
|
}
|
|
|
|
private:
|
|
RefPtr<AsyncReadbackBuffer> mBuffer;
|
|
RefPtr<gfx::DataSourceSurface> mSurface;
|
|
};
|
|
|
|
Compositor::Compositor(widget::CompositorWidget* aWidget)
|
|
: mWidget(aWidget),
|
|
mIsDestroyed(false)
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
// If the default color isn't white for Fennec, there is a black
|
|
// flash before the first page of a tab is loaded.
|
|
,
|
|
mClearColor(ToDeviceColor(sRGBColor::OpaqueWhite()))
|
|
#else
|
|
,
|
|
mClearColor(gfx::DeviceColor())
|
|
#endif
|
|
{
|
|
}
|
|
|
|
Compositor::~Compositor() { ReadUnlockTextures(); }
|
|
|
|
void Compositor::Destroy() {
|
|
mWidget = nullptr;
|
|
|
|
TextureSourceProvider::Destroy();
|
|
mIsDestroyed = true;
|
|
}
|
|
|
|
void Compositor::EndFrame() {
|
|
ReadUnlockTextures();
|
|
mLastCompositionEndTime = TimeStamp::Now();
|
|
}
|
|
|
|
nsTArray<TexturedVertex> TexturedTrianglesToVertexArray(
|
|
const nsTArray<gfx::TexturedTriangle>& aTriangles) {
|
|
const auto VertexFromPoints = [](const gfx::Point& p, const gfx::Point& t) {
|
|
return TexturedVertex{{p.x, p.y}, {t.x, t.y}};
|
|
};
|
|
|
|
nsTArray<TexturedVertex> vertices;
|
|
|
|
for (const gfx::TexturedTriangle& t : aTriangles) {
|
|
vertices.AppendElement(VertexFromPoints(t.p1, t.textureCoords.p1));
|
|
vertices.AppendElement(VertexFromPoints(t.p2, t.textureCoords.p2));
|
|
vertices.AppendElement(VertexFromPoints(t.p3, t.textureCoords.p3));
|
|
}
|
|
|
|
return vertices;
|
|
}
|
|
|
|
static float WrapTexCoord(float v) {
|
|
// This should return values in range [0, 1.0)
|
|
return v - floorf(v);
|
|
}
|
|
|
|
static void SetRects(size_t n, decomposedRectArrayT* aLayerRects,
|
|
decomposedRectArrayT* aTextureRects, float x0, float y0,
|
|
float x1, float y1, float tx0, float ty0, float tx1,
|
|
float ty1, bool flip_y) {
|
|
if (flip_y) {
|
|
std::swap(ty0, ty1);
|
|
}
|
|
(*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0);
|
|
(*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static inline bool FuzzyEqual(float a, float b) {
|
|
return fabs(a - b) < 0.0001f;
|
|
}
|
|
static inline bool FuzzyLTE(float a, float b) { return a <= b + 0.0001f; }
|
|
#endif
|
|
|
|
size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
|
|
const gfx::Rect& aTexCoordRect,
|
|
decomposedRectArrayT* aLayerRects,
|
|
decomposedRectArrayT* aTextureRects) {
|
|
gfx::Rect texCoordRect = aTexCoordRect;
|
|
|
|
// If the texture should be flipped, it will have negative height. Detect that
|
|
// here and compensate for it. We will flip each rect as we emit it.
|
|
bool flipped = false;
|
|
if (texCoordRect.Height() < 0) {
|
|
flipped = true;
|
|
texCoordRect.MoveByY(texCoordRect.Height());
|
|
texCoordRect.SetHeight(-texCoordRect.Height());
|
|
}
|
|
|
|
// Wrap the texture coordinates so they are within [0,1] and cap width/height
|
|
// at 1. We rely on this below.
|
|
texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.X()),
|
|
WrapTexCoord(texCoordRect.Y())),
|
|
gfx::Size(std::min(texCoordRect.Width(), 1.0f),
|
|
std::min(texCoordRect.Height(), 1.0f)));
|
|
|
|
NS_ASSERTION(
|
|
texCoordRect.X() >= 0.0f && texCoordRect.X() <= 1.0f &&
|
|
texCoordRect.Y() >= 0.0f && texCoordRect.Y() <= 1.0f &&
|
|
texCoordRect.Width() >= 0.0f && texCoordRect.Width() <= 1.0f &&
|
|
texCoordRect.Height() >= 0.0f && texCoordRect.Height() <= 1.0f &&
|
|
texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
|
|
texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
|
|
"We just wrapped the texture coordinates, didn't we?");
|
|
|
|
// Get the top left and bottom right points of the rectangle. Note that
|
|
// tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
|
|
gfx::Point tl = texCoordRect.TopLeft();
|
|
gfx::Point br = texCoordRect.BottomRight();
|
|
|
|
NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f && tl.y >= 0.0f && tl.y <= 1.0f &&
|
|
br.x >= tl.x && br.x <= 2.0f && br.y >= tl.y &&
|
|
br.y <= 2.0f && FuzzyLTE(br.x - tl.x, 1.0f) &&
|
|
FuzzyLTE(br.y - tl.y, 1.0f),
|
|
"Somehow generated invalid texture coordinates");
|
|
|
|
// Then check if we wrap in either the x or y axis.
|
|
bool xwrap = br.x > 1.0f;
|
|
bool ywrap = br.y > 1.0f;
|
|
|
|
// If xwrap is false, the texture will be sampled from tl.x .. br.x.
|
|
// If xwrap is true, then it will be split into tl.x .. 1.0, and
|
|
// 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
|
|
// rectangle is also split appropriately, according to the calculated
|
|
// xmid/ymid values.
|
|
if (!xwrap && !ywrap) {
|
|
SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), aRect.XMost(),
|
|
aRect.YMost(), tl.x, tl.y, br.x, br.y, flipped);
|
|
return 1;
|
|
}
|
|
|
|
// If we are dealing with wrapping br.x and br.y are greater than 1.0 so
|
|
// wrap them here as well.
|
|
br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x,
|
|
ywrap ? WrapTexCoord(br.y) : br.y);
|
|
|
|
// If we wrap around along the x axis, we will draw first from
|
|
// tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
|
|
// The same applies for the Y axis. The midpoints we calculate here are
|
|
// only valid if we actually wrap around.
|
|
GLfloat xmid =
|
|
aRect.X() + (1.0f - tl.x) / texCoordRect.Width() * aRect.Width();
|
|
GLfloat ymid =
|
|
aRect.Y() + (1.0f - tl.y) / texCoordRect.Height() * aRect.Height();
|
|
|
|
// Due to floating-point inaccuracy, we have to use XMost()-x and YMost()-y
|
|
// to calculate width and height, respectively, to ensure that size will
|
|
// remain consistent going from absolute to relative and back again.
|
|
NS_ASSERTION(
|
|
!xwrap || (xmid >= aRect.X() && xmid <= aRect.XMost() &&
|
|
FuzzyEqual((xmid - aRect.X()) + (aRect.XMost() - xmid),
|
|
aRect.XMost() - aRect.X())),
|
|
"xmid should be within [x,XMost()] and the wrapped rect should have the "
|
|
"same width");
|
|
NS_ASSERTION(
|
|
!ywrap || (ymid >= aRect.Y() && ymid <= aRect.YMost() &&
|
|
FuzzyEqual((ymid - aRect.Y()) + (aRect.YMost() - ymid),
|
|
aRect.YMost() - aRect.Y())),
|
|
"ymid should be within [y,YMost()] and the wrapped rect should have the "
|
|
"same height");
|
|
|
|
if (!xwrap && ywrap) {
|
|
SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), aRect.XMost(),
|
|
ymid, tl.x, tl.y, br.x, 1.0f, flipped);
|
|
SetRects(1, aLayerRects, aTextureRects, aRect.X(), ymid, aRect.XMost(),
|
|
aRect.YMost(), tl.x, 0.0f, br.x, br.y, flipped);
|
|
return 2;
|
|
}
|
|
|
|
if (xwrap && !ywrap) {
|
|
SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), xmid,
|
|
aRect.YMost(), tl.x, tl.y, 1.0f, br.y, flipped);
|
|
SetRects(1, aLayerRects, aTextureRects, xmid, aRect.Y(), aRect.XMost(),
|
|
aRect.YMost(), 0.0f, tl.y, br.x, br.y, flipped);
|
|
return 2;
|
|
}
|
|
|
|
SetRects(0, aLayerRects, aTextureRects, aRect.X(), aRect.Y(), xmid, ymid,
|
|
tl.x, tl.y, 1.0f, 1.0f, flipped);
|
|
SetRects(1, aLayerRects, aTextureRects, xmid, aRect.Y(), aRect.XMost(), ymid,
|
|
0.0f, tl.y, br.x, 1.0f, flipped);
|
|
SetRects(2, aLayerRects, aTextureRects, aRect.X(), ymid, xmid, aRect.YMost(),
|
|
tl.x, 0.0f, 1.0f, br.y, flipped);
|
|
SetRects(3, aLayerRects, aTextureRects, xmid, ymid, aRect.XMost(),
|
|
aRect.YMost(), 0.0f, 0.0f, br.x, br.y, flipped);
|
|
return 4;
|
|
}
|
|
|
|
void Compositor::UnlockAfterComposition(TextureHost* aTexture) {
|
|
TextureSourceProvider::UnlockAfterComposition(aTexture);
|
|
|
|
// If this is being called after we shutdown the compositor, we must finish
|
|
// read unlocking now to prevent a cycle.
|
|
if (IsDestroyed()) {
|
|
ReadUnlockTextures();
|
|
}
|
|
}
|
|
|
|
bool Compositor::NotifyNotUsedAfterComposition(TextureHost* aTextureHost) {
|
|
if (IsDestroyed() || AsBasicCompositor()) {
|
|
return false;
|
|
}
|
|
return TextureSourceProvider::NotifyNotUsedAfterComposition(aTextureHost);
|
|
}
|
|
|
|
already_AddRefed<RecordedFrame> Compositor::RecordFrame(
|
|
const TimeStamp& aTimeStamp) {
|
|
RefPtr<CompositingRenderTarget> renderTarget = GetWindowRenderTarget();
|
|
if (!renderTarget) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<AsyncReadbackBuffer> buffer =
|
|
CreateAsyncReadbackBuffer(renderTarget->GetSize());
|
|
|
|
if (!ReadbackRenderTarget(renderTarget, buffer)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return MakeAndAddRef<CompositorRecordedFrame>(aTimeStamp, std::move(buffer));
|
|
}
|
|
|
|
bool Compositor::ShouldRecordFrames() const {
|
|
return profiler_feature_active(ProfilerFeature::Screenshots) || mRecordFrames;
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|