commit 96b4eab236cfeb217bf41ba609cb47738fe7a477
parent 86a403a0d1ffba84084bac978873d36fd47a020b
Author: Lee Salzman <lsalzman@mozilla.com>
Date: Tue, 28 Oct 2025 16:54:53 +0000
Bug 1996856 - Avoid DrawTarget::ClearRect when BufferTexture is already clear. r=aosmond
Since BufferTextures are often initialized to a clear value, it is redundant to subsequently
do an initial ClearRect on a subsequent BorrowDrawTarget.
This tracks whether BufferTexture creation initialized the memory to clear and passes this
information to BorrowDrawTarget so that ClearRect can be avoided.
Differential Revision: https://phabricator.services.mozilla.com/D270358
Diffstat:
8 files changed, 54 insertions(+), 24 deletions(-)
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h
@@ -2206,7 +2206,8 @@ class GFX2D_API Factory {
static already_AddRefed<DrawTarget> CreateDrawTargetForData(
BackendType aBackend, unsigned char* aData, const IntSize& aSize,
- int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
+ int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false,
+ bool aIsClear = false);
#ifdef XP_DARWIN
static already_AddRefed<ScaledFont> CreateScaledFontForMacFont(
diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp
@@ -1847,6 +1847,7 @@ bool DrawTargetSkia::Init(const IntSize& aSize, SurfaceFormat aFormat) {
if (info.isOpaque()) {
mCanvas->clear(SK_ColorBLACK);
}
+ mIsClear = true;
return true;
}
@@ -1861,6 +1862,7 @@ bool DrawTargetSkia::Init(SkCanvas* aCanvas) {
SkColor clearColor =
imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
mCanvas->clear(clearColor);
+ mIsClear = true;
}
SkISize size = mCanvas->getBaseLayerSize();
@@ -1874,7 +1876,7 @@ bool DrawTargetSkia::Init(SkCanvas* aCanvas) {
bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
int32_t aStride, SurfaceFormat aFormat,
- bool aUninitialized) {
+ bool aUninitialized, bool aIsClear) {
MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) || aUninitialized ||
VerifyRGBXFormat(aData, aSize, aStride, aFormat));
@@ -1894,6 +1896,7 @@ bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
mFormat = aFormat;
mCanvas = mSurface->getCanvas();
SetPermitSubpixelAA(IsOpaque(mFormat));
+ mIsClear = aIsClear;
return true;
}
@@ -1958,6 +1961,10 @@ already_AddRefed<PathBuilder> DrawTargetSkia::CreatePathBuilder(
}
void DrawTargetSkia::ClearRect(const Rect& aRect) {
+ if (mIsClear) {
+ return;
+ }
+
MarkChanged();
SkPaint paint;
paint.setAntiAlias(true);
@@ -2142,7 +2149,7 @@ already_AddRefed<FilterNode> DrawTargetSkia::CreateFilter(FilterType aType) {
return FilterNodeSoftware::Create(aType);
}
-void DrawTargetSkia::MarkChanged() {
+void DrawTargetSkia::DetachAllSnapshots() {
// I'm not entirely certain whether this lock is needed, as multiple threads
// should never modify the DrawTarget at the same time anyway, but this seems
// like the safest.
@@ -2164,4 +2171,9 @@ void DrawTargetSkia::MarkChanged() {
}
}
+void DrawTargetSkia::MarkChanged() {
+ DetachAllSnapshots();
+ mIsClear = false;
+}
+
} // namespace mozilla::gfx
diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h
@@ -140,11 +140,12 @@ class DrawTargetSkia : public DrawTarget {
virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
virtual void SetTransform(const Matrix& aTransform) override;
virtual void* GetNativeSurface(NativeSurfaceType aType) override;
- virtual void DetachAllSnapshots() override { MarkChanged(); }
+ virtual void DetachAllSnapshots() override;
bool Init(const IntSize& aSize, SurfaceFormat aFormat);
bool Init(unsigned char* aData, const IntSize& aSize, int32_t aStride,
- SurfaceFormat aFormat, bool aUninitialized = false);
+ SurfaceFormat aFormat, bool aUninitialized = false,
+ bool aIsClear = false);
bool Init(SkCanvas* aCanvas);
bool Init(RefPtr<DataSourceSurface>&& aSurface);
@@ -196,6 +197,7 @@ class DrawTargetSkia : public DrawTarget {
RefPtr<DataSourceSurface> mBackingSurface;
RefPtr<SourceSurfaceSkia> mSnapshot;
Mutex mSnapshotLock MOZ_UNANNOTATED;
+ bool mIsClear = false;
#ifdef XP_DARWIN
friend class BorrowedCGContext;
diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp
@@ -432,7 +432,8 @@ already_AddRefed<DrawTarget> Factory::CreateRecordingDrawTarget(
already_AddRefed<DrawTarget> Factory::CreateDrawTargetForData(
BackendType aBackend, unsigned char* aData, const IntSize& aSize,
- int32_t aStride, SurfaceFormat aFormat, bool aUninitialized) {
+ int32_t aStride, SurfaceFormat aFormat, bool aUninitialized,
+ bool aIsClear) {
MOZ_ASSERT(aData);
if (!AllowedSurfaceSize(aSize)) {
gfxCriticalError(LoggerOptionsBasedOnSize(aSize))
@@ -446,7 +447,8 @@ already_AddRefed<DrawTarget> Factory::CreateDrawTargetForData(
case BackendType::SKIA: {
RefPtr<DrawTargetSkia> newTarget;
newTarget = new DrawTargetSkia();
- if (newTarget->Init(aData, aSize, aStride, aFormat, aUninitialized)) {
+ if (newTarget->Init(aData, aSize, aStride, aFormat, aUninitialized,
+ aIsClear)) {
retVal = newTarget;
}
break;
diff --git a/gfx/layers/BufferTexture.cpp b/gfx/layers/BufferTexture.cpp
@@ -49,8 +49,9 @@ class MemoryTextureData : public BufferTextureData {
MemoryTextureData(const BufferDescriptor& aDesc,
gfx::BackendType aMoz2DBackend, uint8_t* aBuffer,
- size_t aBufferSize, bool aAutoDeallocate = false)
- : BufferTextureData(aDesc, aMoz2DBackend),
+ size_t aBufferSize, bool aAutoDeallocate = false,
+ bool aIsClear = false)
+ : BufferTextureData(aDesc, aMoz2DBackend, aIsClear),
mBuffer(aBuffer),
mBufferSize(aBufferSize),
mAutoDeallocate(aAutoDeallocate) {
@@ -96,8 +97,9 @@ class ShmemTextureData : public BufferTextureData {
virtual void Deallocate(LayersIPCChannel* aAllocator) override;
ShmemTextureData(const BufferDescriptor& aDesc,
- gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem)
- : BufferTextureData(aDesc, aMoz2DBackend), mShmem(aShmem) {
+ gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem,
+ bool aIsClear = false)
+ : BufferTextureData(aDesc, aMoz2DBackend, aIsClear), mShmem(aShmem) {
MOZ_ASSERT(mShmem.Size<uint8_t>());
}
@@ -144,7 +146,9 @@ BufferTextureData* BufferTextureData::CreateInternal(
return nullptr;
}
- return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
+ bool isClear = aDesc.type() == BufferDescriptor::TRGBDescriptor &&
+ !IsOpaque(aDesc.get_RGBDescriptor().format());
+ return new ShmemTextureData(aDesc, aMoz2DBackend, shm, isClear);
}
}
@@ -249,14 +253,15 @@ already_AddRefed<gfx::DrawTarget> BufferTextureData::BorrowDrawTarget() {
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
RefPtr<gfx::DrawTarget> dt;
if (gfx::Factory::DoesBackendSupportDataDrawtarget(mMoz2DBackend)) {
- dt = gfx::Factory::CreateDrawTargetForData(
- mMoz2DBackend, GetBuffer(), rgb.size(), stride, rgb.format(), true);
+ dt = gfx::Factory::CreateDrawTargetForData(mMoz2DBackend, GetBuffer(),
+ rgb.size(), stride, rgb.format(),
+ true, mIsClear);
}
if (!dt) {
// Fall back to supported platform backend. Note that mMoz2DBackend
// does not match the draw target type.
dt = gfxPlatform::CreateDrawTargetForData(GetBuffer(), rgb.size(), stride,
- rgb.format(), true);
+ rgb.format(), true, mIsClear);
}
if (!dt) {
@@ -279,6 +284,7 @@ bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) {
aData.format = GetFormat();
aData.stride =
ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
+ mIsClear = false;
return true;
}
@@ -377,6 +383,8 @@ bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
srcSurf->Unmap();
surface->Unmap();
+ mIsClear = false;
+
return true;
}
@@ -447,8 +455,9 @@ MemoryTextureData* MemoryTextureData::Create(gfx::IntSize aSize,
// Remote textures are not managed by a texture client, so we need to ensure
// that memory is freed when the owning MemoryTextureData goes away.
bool autoDeallocate = !!(aFlags & TextureFlags::REMOTE_TEXTURE);
+ bool isClear = (aAllocFlags & ALLOC_CLEAR_BUFFER) != 0;
return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize,
- autoDeallocate);
+ autoDeallocate, isClear);
}
void MemoryTextureData::Deallocate(LayersIPCChannel*) {
@@ -515,8 +524,8 @@ ShmemTextureData* ShmemTextureData::Create(gfx::IntSize aSize,
}
BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
-
- return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
+ bool isClear = (aAllocFlags & ALLOC_CLEAR_BUFFER) || !IsOpaque(aFormat);
+ return new ShmemTextureData(descriptor, aMoz2DBackend, shm, isClear);
}
TextureData* ShmemTextureData::CreateSimilar(
diff --git a/gfx/layers/BufferTexture.h b/gfx/layers/BufferTexture.h
@@ -38,7 +38,7 @@ class BufferTextureData : public TextureData {
bool Lock(OpenMode aMode) override { return true; }
- void Unlock() override {}
+ void Unlock() override { mIsClear = false; }
void FillInfo(TextureData::Info& aInfo) const override;
@@ -93,13 +93,16 @@ class BufferTextureData : public TextureData {
virtual uint8_t* GetBuffer() = 0;
BufferTextureData(const BufferDescriptor& aDescriptor,
- gfx::BackendType aMoz2DBackend)
- : mDescriptor(aDescriptor), mMoz2DBackend(aMoz2DBackend) {}
+ gfx::BackendType aMoz2DBackend, bool aIsClear = false)
+ : mDescriptor(aDescriptor),
+ mMoz2DBackend(aMoz2DBackend),
+ mIsClear(aIsClear) {}
~BufferTextureData() override = default;
BufferDescriptor mDescriptor;
gfx::BackendType mMoz2DBackend;
+ bool mIsClear = false;
};
template <typename ShmemAllocator>
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
@@ -1771,7 +1771,7 @@ already_AddRefed<DrawTarget> gfxPlatform::CreateSimilarSoftwareDrawTarget(
/* static */
already_AddRefed<DrawTarget> gfxPlatform::CreateDrawTargetForData(
unsigned char* aData, const IntSize& aSize, int32_t aStride,
- SurfaceFormat aFormat, bool aUninitialized) {
+ SurfaceFormat aFormat, bool aUninitialized, bool aIsClear) {
BackendType backendType = gfxVars::ContentBackend();
NS_ASSERTION(backendType != BackendType::NONE, "No backend.");
@@ -1780,7 +1780,7 @@ already_AddRefed<DrawTarget> gfxPlatform::CreateDrawTargetForData(
}
RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
- backendType, aData, aSize, aStride, aFormat, aUninitialized);
+ backendType, aData, aSize, aStride, aFormat, aUninitialized, aIsClear);
return dt.forget();
}
diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h
@@ -284,7 +284,8 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener {
static already_AddRefed<DrawTarget> CreateDrawTargetForData(
unsigned char* aData, const mozilla::gfx::IntSize& aSize, int32_t aStride,
- mozilla::gfx::SurfaceFormat aFormat, bool aUninitialized = false);
+ mozilla::gfx::SurfaceFormat aFormat, bool aUninitialized = false,
+ bool aIsClear = false);
/**
* Returns true if we should use Azure to render content with aTarget. For