Bug 1405927 - Change PushGlyphs to take webrender formats. r?jrmuizel
Also cleans up a bunch of TextDrawTarget code as fallout.
This is a significant perf win for textFrames.
MozReview-Commit-ID: J1BDkXZdvnc
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -241,41 +241,32 @@ WriteFontFileData(const uint8_t* aData,
return;
}
memcpy(data->mFontBuffer.mData, aData, aLength);
data->mFontIndex = aIndex;
}
void
-WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
- gfx::ScaledFont* aFont, const gfx::Color& aColor, const StackingContextHelper& aSc,
- const LayerRect& aBounds, const LayerRect& aClip, bool aBackfaceVisible)
+WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<wr::GlyphInstance>& aGlyphs,
+ gfx::ScaledFont* aFont, const wr::ColorF& aColor, const StackingContextHelper& aSc,
+ const wr::LayerRect& aBounds, const wr::LayerRect& aClip, bool aBackfaceVisible)
{
MOZ_ASSERT(aFont);
MOZ_ASSERT(!aGlyphs.IsEmpty());
wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont);
MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
- nsTArray<wr::GlyphInstance> wr_glyph_instances;
- wr_glyph_instances.SetLength(aGlyphs.Length());
-
- for (size_t j = 0; j < aGlyphs.Length(); j++) {
- wr_glyph_instances[j].index = aGlyphs[j].mIndex;
- wr_glyph_instances[j].point = aSc.ToRelativeLayoutPoint(
- LayerPoint::FromUnknownPoint(aGlyphs[j].mPosition));
- }
-
- aBuilder.PushText(aSc.ToRelativeLayoutRect(aBounds),
- aSc.ToRelativeLayoutRect(aClip),
+ aBuilder.PushText(aBounds,
+ aClip,
aBackfaceVisible,
aColor,
key,
- Range<const wr::GlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()));
+ Range<const wr::GlyphInstance>(aGlyphs.Elements(), aGlyphs.Length()));
}
wr::FontInstanceKey
WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont)
{
MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(aScaledFont);
MOZ_ASSERT((aScaledFont->GetType() == gfx::FontType::DWRITE) ||
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -109,20 +109,20 @@ public:
mIdNamespace = aIdNamespace;
}
wr::WrImageKey GetNextImageKey()
{
return wr::WrImageKey{ GetNamespace(), GetNextResourceId() };
}
- void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
- gfx::ScaledFont* aFont, const gfx::Color& aColor,
+ void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<wr::GlyphInstance>& aGlyphs,
+ gfx::ScaledFont* aFont, const wr::ColorF& aColor,
const StackingContextHelper& aSc,
- const LayerRect& aBounds, const LayerRect& aClip,
+ const wr::LayerRect& aBounds, const wr::LayerRect& aClip,
bool aBackfaceVisible);
wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
void RemoveExpiredFontKeys();
void ClearReadLocks();
void BeginClearCachedResources();
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -1036,23 +1036,23 @@ DisplayListBuilder::PushBorderRadialGrad
aRadius, aStops.Elements(), aStops.Length(),
aExtendMode, aOutset);
}
void
DisplayListBuilder::PushText(const wr::LayoutRect& aBounds,
const wr::LayoutRect& aClip,
bool aIsBackfaceVisible,
- const gfx::Color& aColor,
+ const wr::ColorF& aColor,
wr::FontInstanceKey aFontKey,
Range<const wr::GlyphInstance> aGlyphBuffer,
const wr::GlyphOptions* aGlyphOptions)
{
wr_dp_push_text(mWrState, aBounds, aClip, aIsBackfaceVisible,
- ToColorF(aColor),
+ aColor,
aFontKey,
&aGlyphBuffer[0], aGlyphBuffer.length(),
aGlyphOptions);
}
void
DisplayListBuilder::PushLine(const wr::LayoutRect& aClip,
bool aIsBackfaceVisible,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -353,17 +353,17 @@ public:
const wr::LayoutSize& aRadius,
const nsTArray<wr::GradientStop>& aStops,
wr::ExtendMode aExtendMode,
const wr::SideOffsets2D_f32& aOutset);
void PushText(const wr::LayoutRect& aBounds,
const wr::LayoutRect& aClip,
bool aIsBackfaceVisible,
- const gfx::Color& aColor,
+ const wr::ColorF& aColor,
wr::FontInstanceKey aFontKey,
Range<const wr::GlyphInstance> aGlyphBuffer,
const wr::GlyphOptions* aGlyphOptions = nullptr);
void PushLine(const wr::LayoutRect& aClip,
bool aIsBackfaceVisible,
const wr::Line& aLine);
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -15,18 +15,18 @@
namespace mozilla {
namespace layout {
using namespace gfx;
// This is used by all Advanced Layers users, so we use plain gfx types
struct TextRunFragment {
ScaledFont* font;
- Color color;
- nsTArray<gfx::Glyph> glyphs;
+ wr::ColorF color;
+ nsTArray<wr::GlyphInstance> glyphs;
};
// Only webrender handles this, so we use webrender types
struct SelectionFragment {
wr::ColorF color;
wr::LayoutRect rect;
};
@@ -44,22 +44,37 @@ struct SelectionFragment {
//
// Would be broken up into 5 SelectedTextRunFragments
//
// ["Hello ", "there", " my name ", "is Mega", "man"]
//
// For almost all nsTextFrames, there will be only one SelectedTextRunFragment.
struct SelectedTextRunFragment {
Maybe<SelectionFragment> selection;
- nsTArray<wr::Shadow> shadows;
- nsTArray<TextRunFragment> text;
- nsTArray<wr::Line> beforeDecorations;
- nsTArray<wr::Line> afterDecorations;
+ AutoTArray<wr::Shadow, 1> shadows;
+ AutoTArray<TextRunFragment, 1> text;
+ AutoTArray<wr::Line, 1> beforeDecorations;
+ AutoTArray<wr::Line, 1> afterDecorations;
};
+}
+}
+
+// AutoTArray is bad
+template<>
+struct nsTArray_CopyChooser<mozilla::layout::SelectedTextRunFragment>
+{
+ typedef nsTArray_CopyWithConstructors<mozilla::layout::SelectedTextRunFragment> Type;
+};
+
+namespace mozilla {
+namespace layout {
+
+using namespace gfx;
+
// This class is fake DrawTarget, used to intercept text draw calls, while
// also collecting up the other aspects of text natively.
//
// When using advanced-layers in nsDisplayText's constructor, we construct this
// and run the full painting algorithm with this as the DrawTarget. This is
// done to avoid having to massively refactor gecko's text painting code (which
// has lots of components shared between other rendering algorithms).
//
@@ -86,18 +101,20 @@ class TextDrawTarget : public DrawTarget
{
public:
// The different phases of drawing the text we're in
// Each should only happen once, and in the given order.
enum class Phase : uint8_t {
eSelection, eUnderline, eOverline, eGlyphs, eEmphasisMarks, eLineThrough
};
- explicit TextDrawTarget()
- : mCurrentlyDrawing(Phase::eSelection), mHasUnsupportedFeatures(false)
+ explicit TextDrawTarget(const layers::StackingContextHelper& aSc)
+ : mCurrentlyDrawing(Phase::eSelection),
+ mHasUnsupportedFeatures(false),
+ mSc(aSc)
{
SetSelectionIndex(0);
}
// Prevent this from being copied
TextDrawTarget(const TextDrawTarget& src) = delete;
TextDrawTarget& operator=(const TextDrawTarget&) = delete;
@@ -142,29 +159,36 @@ public:
MOZ_CRASH("TextDrawTarget received glyphs in wrong phase");
}
// We need to push a new TextRunFragment whenever the font/color changes
// (usually this implies some font fallback from mixing languages/emoji)
TextRunFragment* fragment;
if (mCurrentPart->text.IsEmpty() ||
mCurrentPart->text.LastElement().font != aFont ||
- mCurrentPart->text.LastElement().color != colorPat->mColor) {
+ !(mCurrentPart->text.LastElement().color == wr::ToColorF(colorPat->mColor))) {
fragment = mCurrentPart->text.AppendElement();
fragment->font = aFont;
- fragment->color = colorPat->mColor;
+ fragment->color = wr::ToColorF(colorPat->mColor);
} else {
fragment = &mCurrentPart->text.LastElement();
}
- nsTArray<Glyph>& glyphs = fragment->glyphs;
+ nsTArray<wr::GlyphInstance>& glyphs = fragment->glyphs;
size_t oldLength = glyphs.Length();
glyphs.SetLength(oldLength + aBuffer.mNumGlyphs);
- PodCopy(glyphs.Elements() + oldLength, aBuffer.mGlyphs, aBuffer.mNumGlyphs);
+
+ for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
+ wr::GlyphInstance& targetGlyph = glyphs[oldLength + i];
+ const gfx::Glyph& sourceGlyph = aBuffer.mGlyphs[i];
+ targetGlyph.index = sourceGlyph.mIndex;
+ targetGlyph.point = mSc.ToRelativeLayoutPoint(
+ LayerPoint::FromUnknownPoint(sourceGlyph.mPosition));
+ }
}
void
AppendShadow(const wr::Shadow& aShadow) {
mCurrentPart->shadows.AppendElement(aShadow);
}
void
@@ -253,59 +277,27 @@ public:
if (!frag.font->CanSerialize()) {
return false;
}
}
}
return true;
}
- // TextLayers don't support very complicated text right now. This checks
- // if any of the problem cases exist.
bool
- ContentsAreSimple()
- {
-
- ScaledFont* font = nullptr;
-
- for (const SelectedTextRunFragment& part : GetParts()) {
- // Can't handle shadows, selections, or decorations
- if (part.shadows.Length() > 0 ||
- part.beforeDecorations.Length() > 0 ||
- part.afterDecorations.Length() > 0 ||
- part.selection.isSome()) {
- return false;
- }
-
- // Must only have one font (multiple colors is fine)
- for (const mozilla::layout::TextRunFragment& text : part.text) {
- if (!font) {
- font = text.font;
- }
- if (font != text.font) {
- return false;
- }
- }
- }
-
- // Must have an actual font (i.e. actual text)
- if (!font) {
- return false;
- }
-
- return true;
- }
-
- void
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
const layers::StackingContextHelper& aSc,
layers::WebRenderLayerManager* aManager,
nsDisplayItem* aItem,
nsRect& aBounds) {
+ if (!CanSerializeFonts()) {
+ return false;
+ }
+
// Drawing order: selections,
// shadows,
// underline, overline, [grouped in one array]
// text, emphasisText, [grouped in one array]
// lineThrough
// Compute clip/bounds
auto appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
@@ -343,44 +335,50 @@ public:
}
for (const wr::Line& decoration : part.beforeDecorations) {
aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
}
for (const mozilla::layout::TextRunFragment& text : part.text) {
aManager->WrBridge()->PushGlyphs(aBuilder, text.glyphs, text.font,
- text.color, aSc, boundsRect, clipRect,
+ text.color, aSc, wrBoundsRect, wrClipRect,
backfaceVisible);
}
for (const wr::Line& decoration : part.afterDecorations) {
aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
}
for (size_t i = 0; i < part.shadows.Length(); ++i) {
aBuilder.PopShadow();
}
}
+
+ return true;
}
-
private:
// The part of the text we're currently drawing (glyphs, underlines, etc.)
Phase mCurrentlyDrawing;
// Which chunk of mParts is actively being populated
SelectedTextRunFragment* mCurrentPart;
// Chunks of the text, grouped by selection
- nsTArray<SelectedTextRunFragment> mParts;
+ AutoTArray<SelectedTextRunFragment, 1> mParts;
// Whether Tofu or SVG fonts were encountered
bool mHasUnsupportedFeatures;
+ // Needs to be saved so FillGlyphs can use this to offset glyphs to
+ // relative space. Shouldn't be used otherwise (may dangle if we move
+ // to retaining TextDrawTargets)
+ const layers::StackingContextHelper& mSc;
+
// The rest of this is dummy implementations of DrawTarget's API
public:
DrawTargetType GetType() const override {
return DrawTargetType::SOFTWARE_RASTER;
}
BackendType GetBackendType() const override {
return BackendType::WEBRENDER_TEXT;
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -290,42 +290,34 @@ nsDisplayTextOverflowMarker::PaintTextTo
bool
nsDisplayTextOverflowMarker::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
- if (!gfxPrefs::LayersAllowTextLayers() ||
- !CanUseAdvancedLayer(aDisplayListBuilder->GetWidgetLayerManager())) {
+ if (!gfxPrefs::LayersAllowTextLayers()) {
return false;
}
bool snap;
nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
if (bounds.IsEmpty()) {
return true;
}
// Run the rendering algorithm to capture the glyphs and shadows
- RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget();
+ RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aSc);
RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
// TextOverflowMarker only draws glyphs
textDrawer->StartDrawing(TextDrawTarget::Phase::eGlyphs);
Paint(aDisplayListBuilder, captureCtx);
- if (!textDrawer->CanSerializeFonts()) {
- return false;
- }
-
- textDrawer->CreateWebRenderCommands(aBuilder, aSc, aManager, this, bounds);
-
-
- return true;
+ return textDrawer->CreateWebRenderCommands(aBuilder, aSc, aManager, this, bounds);
}
TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder,
nsIFrame* aBlockFrame)
: mContentArea(aBlockFrame->GetWritingMode(),
aBlockFrame->GetContentRectRelativeToSelf(),
aBlockFrame->GetSize())
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -516,21 +516,34 @@ BulletRenderer::CreateWebRenderCommandsF
MOZ_ASSERT(!mGlyphs.IsEmpty());
const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
bool dummy;
LayerRect destRect = ViewAs<LayerPixel>(
LayoutDeviceRect::FromAppUnits(
aItem->GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel),
PixelCastJustification::WebRenderHasUnitResolution);
+ wr::LayoutRect wrDestRect = aSc.ToRelativeLayoutRect(destRect);
- for (layers::GlyphArray& glyphs : mGlyphs) {
- aManager->WrBridge()->PushGlyphs(aBuilder, glyphs.glyphs(), mFont,
- glyphs.color().value(),
- aSc, destRect, destRect, !aItem->BackfaceIsHidden());
+ nsTArray<wr::GlyphInstance> wrGlyphs;
+
+ for (layers::GlyphArray& glyphArray : mGlyphs) {
+ const auto& glyphs = glyphArray.glyphs();
+ wrGlyphs.SetLength(glyphs.Length());
+
+ for (size_t j = 0; j < glyphs.Length(); j++) {
+ wrGlyphs[j].index = glyphs[j].mIndex;
+ wrGlyphs[j].point = aSc.ToRelativeLayoutPoint(
+ LayerPoint::FromUnknownPoint(glyphs[j].mPosition));
+ }
+
+ aManager->WrBridge()->PushGlyphs(aBuilder, wrGlyphs, mFont,
+ wr::ToColorF(glyphArray.color().value()),
+ aSc, wrDestRect, wrDestRect,
+ !aItem->BackfaceIsHidden());
}
}
class nsDisplayBullet final : public nsDisplayItem {
public:
nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
: nsDisplayItem(aBuilder, aFrame)
{
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5044,17 +5044,16 @@ public:
nsCString buf;
int32_t totalContentLength;
f->ToCString(buf, &totalContentLength);
aStream << buf.get() << "\")";
#endif
}
- RefPtr<TextDrawTarget> mTextDrawer;
nsRect mBounds;
float mOpacity;
};
class nsDisplayTextGeometry : public nsCharClipGeometry
{
public:
nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
@@ -5145,29 +5144,23 @@ nsDisplayText::CreateWebRenderCommands(m
if (!gfxPrefs::LayersAllowTextLayers()) {
return false;
}
if (mBounds.IsEmpty()) {
return true;
}
- RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget();
+ RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aSc);
RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
// TODO: Paint() checks mDisableSubpixelAA, we should too.
RenderToContext(captureCtx, aDisplayListBuilder, true);
- if (!textDrawer->CanSerializeFonts()) {
- return false;
- }
-
- textDrawer->CreateWebRenderCommands(aBuilder, aSc, aManager, this, mBounds);
-
- return true;
+ return textDrawer->CreateWebRenderCommands(aBuilder, aSc, aManager, this, mBounds);
}
void
nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording)
{
nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
// Add 1 pixel of dirty area around mVisibleRect to allow us to paint