Bug 1617493 - Rasterize SVG patterns in content process into a shared surface for WebRender. r=jrmuizel

In DrawTargetRecording::CreateSimilarDrawTarget, we would originally
create a new DrawTargetRecording. For resources that are shared, such as
SVG patterns, we would do the same work for each blob image tile. This
can get expensive if the pattern is large. Now we check a size
threshold, and if passed, we create a DrawTargetSkia backed by a
SourceSurfaceSharedData. When we want a snapshot, we now try to get the
shared surface instead if available. The recording infrastructure
already knows how to handle the lifetimes and use of external IDs for
shared surfaces, so now we rasterize the pattern once for all the blob
tiles. In an ideal world we would do this in the compositor process
once, but that requires more changes, and this is useful as a stopgap in
the meantime.

Differential Revision: https://phabricator.services.mozilla.com/D63903

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrew Osmond 2020-02-25 13:56:23 +00:00
parent 92625e728c
commit fbaa72bc71
6 changed files with 109 additions and 2 deletions

View File

@ -1072,6 +1072,16 @@ class DrawTarget : public external::AtomicRefCounted<DrawTarget> {
*/
virtual already_AddRefed<SourceSurface> Snapshot() = 0;
/**
* Returns a SourceSurface which wraps the buffer backing the DrawTarget. The
* contents of the buffer may change if there are drawing operations after
* calling but only guarantees that it reflects the state at the time it was
* called.
*/
virtual already_AddRefed<SourceSurface> GetBackingSurface() {
return Snapshot();
}
// Snapshots the contents and returns an alpha mask
// based on the RGB values.
virtual already_AddRefed<SourceSurface> IntoLuminanceSource(
@ -1453,6 +1463,15 @@ class DrawTarget : public external::AtomicRefCounted<DrawTarget> {
virtual already_AddRefed<DrawTarget> CreateSimilarDrawTarget(
const IntSize& aSize, SurfaceFormat aFormat) const = 0;
/**
* Create a DrawTarget whose backing surface is optimized for use with this
* DrawTarget.
*/
virtual already_AddRefed<DrawTarget> CreateSimilarDrawTargetWithBacking(
const IntSize& aSize, SurfaceFormat aFormat) const {
return CreateSimilarDrawTarget(aSize, aFormat);
}
/**
* Create a DrawTarget whose snapshot is optimized for use with this
* DrawTarget and aFilter.

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DrawTargetRecording.h"
#include "DrawTargetSkia.h"
#include "PathRecording.h"
#include <stdio.h>
@ -12,6 +13,7 @@
#include "Tools.h"
#include "Filters.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/layers/SourceSurfaceSharedData.h"
#include "mozilla/UniquePtr.h"
#include "RecordingTypes.h"
#include "RecordedEventImpl.h"
@ -496,6 +498,34 @@ DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(
return nullptr;
}
already_AddRefed<DrawTarget>
DrawTargetRecording::CreateSimilarDrawTargetWithBacking(
const IntSize& aSize, SurfaceFormat aFormat) const {
RefPtr<DrawTarget> similarDT;
if (mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat)) {
// If the requested similar draw target is too big, then we should try to
// rasterize on the content side to avoid duplicating the effort when a
// blob image gets tiled. If we fail somehow to produce it, we can fall
// back to recording.
constexpr int32_t kRasterThreshold = 256 * 256 * 4;
int32_t stride = aSize.width * BytesPerPixel(aFormat);
int32_t surfaceBytes = aSize.height * stride;
if (surfaceBytes >= kRasterThreshold) {
auto surface = MakeRefPtr<SourceSurfaceSharedData>();
if (surface->Init(aSize, stride, aFormat)) {
auto dt = MakeRefPtr<DrawTargetSkia>();
if (dt->Init(std::move(surface))) {
return dt.forget();
} else {
MOZ_ASSERT_UNREACHABLE("Skia should initialize given surface!");
}
}
}
}
return CreateSimilarDrawTarget(aSize, aFormat);
}
already_AddRefed<DrawTarget> DrawTargetRecording::CreateSimilarDrawTarget(
const IntSize& aSize, SurfaceFormat aFormat) const {
RefPtr<DrawTarget> similarDT;

View File

@ -291,6 +291,13 @@ class DrawTargetRecording : public DrawTarget {
virtual already_AddRefed<DrawTarget> CreateSimilarDrawTarget(
const IntSize& aSize, SurfaceFormat aFormat) const override;
/**
* Create a DrawTarget whose backing surface is optimized for use with this
* DrawTarget.
*/
virtual already_AddRefed<DrawTarget> CreateSimilarDrawTargetWithBacking(
const IntSize& aSize, SurfaceFormat aFormat) const override;
bool CanCreateSimilarDrawTarget(const IntSize& aSize,
SurfaceFormat aFormat) const override;
/**

View File

@ -320,6 +320,14 @@ already_AddRefed<SourceSurface> DrawTargetSkia::Snapshot() {
return snapshot.forget();
}
already_AddRefed<SourceSurface> DrawTargetSkia::GetBackingSurface() {
if (mBackingSurface) {
RefPtr<SourceSurface> snapshot = mBackingSurface;
return snapshot.forget();
}
return Snapshot();
}
bool DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride,
SurfaceFormat* aFormat, IntPoint* aOrigin) {
SkImageInfo info;
@ -1743,6 +1751,43 @@ bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
return true;
}
bool DrawTargetSkia::Init(RefPtr<DataSourceSurface>&& aSurface) {
auto map =
new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
if (!map->IsMapped()) {
delete map;
return false;
}
SurfaceFormat format = aSurface->GetFormat();
IntSize size = aSurface->GetSize();
MOZ_ASSERT((format != SurfaceFormat::B8G8R8X8) ||
VerifyRGBXFormat(map->GetData(), size, map->GetStride(), format));
SkSurfaceProps props(0, GetSkPixelGeometry());
mSurface = SkSurface::MakeRasterDirectReleaseProc(
MakeSkiaImageInfo(size, format), map->GetData(), map->GetStride(),
DrawTargetSkia::ReleaseMappedSkSurface, map, &props);
if (!mSurface) {
delete map;
return false;
}
// map is now owned by mSurface
mBackingSurface = std::move(aSurface);
mSize = size;
mFormat = format;
mCanvas = mSurface->getCanvas();
SetPermitSubpixelAA(IsOpaque(format));
return true;
}
/* static */ void DrawTargetSkia::ReleaseMappedSkSurface(void* aPixels,
void* aContext) {
auto map = reinterpret_cast<DataSourceSurface::ScopedMap*>(aContext);
delete map;
}
void DrawTargetSkia::SetTransform(const Matrix& aTransform) {
SkMatrix mat;
GfxMatrixToSkiaMatrix(aTransform, mat);

View File

@ -24,6 +24,7 @@
namespace mozilla {
namespace gfx {
class DataSourceSurface;
class SourceSurfaceSkia;
class BorrowedCGContext;
@ -38,6 +39,7 @@ class DrawTargetSkia : public DrawTarget {
return BackendType::SKIA;
}
virtual already_AddRefed<SourceSurface> Snapshot() override;
already_AddRefed<SourceSurface> GetBackingSurface() override;
virtual IntSize GetSize() const override { return mSize; };
virtual bool LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride,
SurfaceFormat* aFormat,
@ -132,6 +134,7 @@ class DrawTargetSkia : public DrawTarget {
bool Init(unsigned char* aData, const IntSize& aSize, int32_t aStride,
SurfaceFormat aFormat, bool aUninitialized = false);
bool Init(SkCanvas* aCanvas);
bool Init(RefPtr<DataSourceSurface>&& aSurface);
// Skia assumes that texture sizes fit in 16-bit signed integers.
static size_t GetMaxSurfaceSize() { return 32767; }
@ -145,6 +148,8 @@ class DrawTargetSkia : public DrawTarget {
private:
friend class SourceSurfaceSkia;
static void ReleaseMappedSkSurface(void* aPixels, void* aContext);
void MarkChanged();
void DrawGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
@ -163,6 +168,7 @@ class DrawTargetSkia : public DrawTarget {
IntSize mSize;
sk_sp<SkSurface> mSurface;
SkCanvas* mCanvas;
RefPtr<DataSourceSurface> mBackingSurface;
RefPtr<SourceSurfaceSkia> mSnapshot;
Mutex mSnapshotLock;

View File

@ -326,7 +326,7 @@ already_AddRefed<SourceSurface> nsSVGPatternFrame::PaintPattern(
patternHeight / surfaceSize.height);
}
RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTarget(
RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTargetWithBacking(
surfaceSize, SurfaceFormat::B8G8R8A8);
if (!dt || !dt->IsValid()) {
return nullptr;
@ -377,7 +377,7 @@ already_AddRefed<SourceSurface> nsSVGPatternFrame::PaintPattern(
}
// caller now owns the surface
return dt->Snapshot();
return dt->GetBackingSurface();
}
/* Will probably need something like this... */