mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
355 lines
12 KiB
C++
355 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* 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 "base/message_loop.h" // for MessageLoop
|
|
#include "mozilla/layers/CompositorParent.h" // for CompositorParent
|
|
#include "mozilla/layers/Effects.h" // for Effect, EffectChain, etc
|
|
#include "mozilla/mozalloc.h" // for operator delete, etc
|
|
#include "gfx2DGlue.h"
|
|
#include "nsAppRunner.h"
|
|
|
|
namespace mozilla {
|
|
namespace gfx {
|
|
class Matrix4x4;
|
|
}
|
|
|
|
namespace layers {
|
|
|
|
/* static */ LayersBackend Compositor::sBackend = LayersBackend::LAYERS_NONE;
|
|
/* static */ LayersBackend
|
|
Compositor::GetBackend()
|
|
{
|
|
if (sBackend != LayersBackend::LAYERS_NONE) {
|
|
AssertOnCompositorThread();
|
|
}
|
|
return sBackend;
|
|
}
|
|
|
|
/* static */ void
|
|
Compositor::SetBackend(LayersBackend backend)
|
|
{
|
|
if (!gIsGtest && sBackend != backend &&
|
|
sBackend != LayersBackend::LAYERS_NONE &&
|
|
backend != LayersBackend::LAYERS_NONE) {
|
|
// Assert this once we figure out bug 972891.
|
|
#ifdef XP_MACOSX
|
|
gfxWarning() << "Changing compositor from " << unsigned(sBackend) << " to " << unsigned(backend);
|
|
#endif
|
|
}
|
|
|
|
sBackend = backend;
|
|
}
|
|
|
|
/* static */ void
|
|
Compositor::AssertOnCompositorThread()
|
|
{
|
|
MOZ_ASSERT(!CompositorParent::CompositorLoop() ||
|
|
CompositorParent::CompositorLoop() == MessageLoop::current(),
|
|
"Can only call this from the compositor thread!");
|
|
}
|
|
|
|
bool
|
|
Compositor::ShouldDrawDiagnostics(DiagnosticFlags aFlags)
|
|
{
|
|
if ((aFlags & DiagnosticFlags::TILE) && !(mDiagnosticTypes & DiagnosticTypes::TILE_BORDERS)) {
|
|
return false;
|
|
}
|
|
if ((aFlags & DiagnosticFlags::BIGIMAGE) &&
|
|
!(mDiagnosticTypes & DiagnosticTypes::BIGIMAGE_BORDERS)) {
|
|
return false;
|
|
}
|
|
if (mDiagnosticTypes == DiagnosticTypes::NO_DIAGNOSTIC) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
|
|
const nsIntRegion& aVisibleRegion,
|
|
const gfx::Rect& aClipRect,
|
|
const gfx::Matrix4x4& aTransform,
|
|
uint32_t aFlashCounter)
|
|
{
|
|
if (!ShouldDrawDiagnostics(aFlags)) {
|
|
return;
|
|
}
|
|
|
|
if (aVisibleRegion.GetNumRects() > 1) {
|
|
nsIntRegionRectIterator screenIter(aVisibleRegion);
|
|
|
|
while (const nsIntRect* rect = screenIter.Next())
|
|
{
|
|
DrawDiagnostics(aFlags | DiagnosticFlags::REGION_RECT,
|
|
ToRect(*rect), aClipRect, aTransform, aFlashCounter);
|
|
}
|
|
}
|
|
|
|
DrawDiagnostics(aFlags, ToRect(aVisibleRegion.GetBounds()),
|
|
aClipRect, aTransform, aFlashCounter);
|
|
}
|
|
|
|
void
|
|
Compositor::DrawDiagnostics(DiagnosticFlags aFlags,
|
|
const gfx::Rect& aVisibleRect,
|
|
const gfx::Rect& aClipRect,
|
|
const gfx::Matrix4x4& aTransform,
|
|
uint32_t aFlashCounter)
|
|
{
|
|
if (!ShouldDrawDiagnostics(aFlags)) {
|
|
return;
|
|
}
|
|
|
|
DrawDiagnosticsInternal(aFlags, aVisibleRect, aClipRect, aTransform,
|
|
aFlashCounter);
|
|
}
|
|
|
|
void
|
|
Compositor::DrawDiagnosticsInternal(DiagnosticFlags aFlags,
|
|
const gfx::Rect& aVisibleRect,
|
|
const gfx::Rect& aClipRect,
|
|
const gfx::Matrix4x4& aTransform,
|
|
uint32_t aFlashCounter)
|
|
{
|
|
#ifdef MOZ_B2G
|
|
int lWidth = 4;
|
|
#elif defined(ANDROID)
|
|
int lWidth = 10;
|
|
#else
|
|
int lWidth = 2;
|
|
#endif
|
|
float opacity = 0.7f;
|
|
|
|
gfx::Color color;
|
|
if (aFlags & DiagnosticFlags::CONTENT) {
|
|
color = gfx::Color(0.0f, 1.0f, 0.0f, 1.0f); // green
|
|
if (aFlags & DiagnosticFlags::COMPONENT_ALPHA) {
|
|
color = gfx::Color(0.0f, 1.0f, 1.0f, 1.0f); // greenish blue
|
|
}
|
|
} else if (aFlags & DiagnosticFlags::IMAGE) {
|
|
color = gfx::Color(1.0f, 0.0f, 0.0f, 1.0f); // red
|
|
} else if (aFlags & DiagnosticFlags::COLOR) {
|
|
color = gfx::Color(0.0f, 0.0f, 1.0f, 1.0f); // blue
|
|
} else if (aFlags & DiagnosticFlags::CONTAINER) {
|
|
color = gfx::Color(0.8f, 0.0f, 0.8f, 1.0f); // purple
|
|
}
|
|
|
|
// make tile borders a bit more transparent to keep layer borders readable.
|
|
if (aFlags & DiagnosticFlags::TILE ||
|
|
aFlags & DiagnosticFlags::BIGIMAGE ||
|
|
aFlags & DiagnosticFlags::REGION_RECT) {
|
|
lWidth = 1;
|
|
opacity = 0.5f;
|
|
color.r *= 0.7f;
|
|
color.g *= 0.7f;
|
|
color.b *= 0.7f;
|
|
}
|
|
|
|
if (mDiagnosticTypes & DiagnosticTypes::FLASH_BORDERS) {
|
|
float flash = (float)aFlashCounter / (float)DIAGNOSTIC_FLASH_COUNTER_MAX;
|
|
color.r *= flash;
|
|
color.g *= flash;
|
|
color.b *= flash;
|
|
}
|
|
|
|
EffectChain effects;
|
|
|
|
effects.mPrimaryEffect = new EffectSolidColor(color);
|
|
// left
|
|
this->DrawQuad(gfx::Rect(aVisibleRect.x, aVisibleRect.y,
|
|
lWidth, aVisibleRect.height),
|
|
aClipRect, effects, opacity,
|
|
aTransform);
|
|
// top
|
|
this->DrawQuad(gfx::Rect(aVisibleRect.x + lWidth, aVisibleRect.y,
|
|
aVisibleRect.width - 2 * lWidth, lWidth),
|
|
aClipRect, effects, opacity,
|
|
aTransform);
|
|
// right
|
|
this->DrawQuad(gfx::Rect(aVisibleRect.x + aVisibleRect.width - lWidth, aVisibleRect.y,
|
|
lWidth, aVisibleRect.height),
|
|
aClipRect, effects, opacity,
|
|
aTransform);
|
|
// bottom
|
|
this->DrawQuad(gfx::Rect(aVisibleRect.x + lWidth, aVisibleRect.y + aVisibleRect.height-lWidth,
|
|
aVisibleRect.width - 2 * lWidth, lWidth),
|
|
aClipRect, effects, opacity,
|
|
aTransform);
|
|
}
|
|
|
|
static float
|
|
WrapTexCoord(float v)
|
|
{
|
|
// fmodf gives negative results for negative numbers;
|
|
// that is, fmodf(0.75, 1.0) == 0.75, but
|
|
// fmodf(-0.75, 1.0) == -0.75. For the negative case,
|
|
// the result we need is 0.25, so we add 1.0f.
|
|
if (v < 0.0f) {
|
|
return 1.0f + fmodf(v, 1.0f);
|
|
}
|
|
|
|
return fmodf(v, 1.0f);
|
|
}
|
|
|
|
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.y += texCoordRect.height;
|
|
texCoordRect.height = -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;
|
|
|
|
NS_ASSERTION(!xwrap ||
|
|
(xmid > aRect.x &&
|
|
xmid < aRect.XMost() &&
|
|
FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)),
|
|
"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.height)),
|
|
"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;
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|